Skip to content

Async Operations

Asynchronous operation patterns for long-running tasks

Extension URN: urn:forrst:ext:async


Async operations allow functions to return immediately while processing continues in the background. Clients poll for completion or receive callbacks.

This extension provides three management functions for operation lifecycle control:

FunctionDescription
urn:cline:forrst:ext:async:fn:statusCheck operation status and progress
urn:cline:forrst:ext:async:fn:cancelCancel a pending/processing operation
urn:cline:forrst:ext:async:fn:listList operations for the current caller

These functions are only available when the async extension is enabled. Servers not implementing async operations SHOULD NOT register these functions.


Async operations SHOULD be used for:

  • Report generation
  • Bulk data processing
  • External API calls with long latency
  • Operations exceeding typical deadline

Async operations SHOULD NOT be used for:

  • Simple CRUD operations
  • Low-latency requirements
  • Operations without observable progress

Clients MAY request async handling via the async extension:

{
"protocol": { "name": "forrst", "version": "0.1.0" },
"id": "req_123",
"call": {
"function": "reports.generate",
"version": "1.0.0",
"arguments": {
"type": "annual",
"year": 2024
}
},
"extensions": [
{
"urn": "urn:forrst:ext:async",
"options": {
"preferred": true
}
}
]
}
FieldTypeDescription
preferredbooleanClient prefers async if operation is long-running
callback_urlstringURL to POST result when complete (optional)

When the server accepts the request for async processing:

{
"protocol": { "name": "forrst", "version": "0.1.0" },
"id": "req_123",
"result": null,
"extensions": [
{
"urn": "urn:forrst:ext:async",
"data": {
"operation_id": "op_xyz789",
"status": "processing",
"poll": {
"function": "urn:cline:forrst:ext:async:fn:status",
"version": "1.0.0",
"arguments": { "operation_id": "op_xyz789" }
},
"retry_after": { "value": 5, "unit": "second" }
}
}
]
}
FieldTypeRequiredDescription
operation_idstringYesUnique identifier for the operation
statusstringYesCurrent status (see below)
pollobjectYesFunction call to check status
retry_afterobjectNoSuggested wait before next poll
progressnumberNoCompletion percentage (0.0 to 1.0)
started_atstringNoISO 8601 timestamp when processing started
StatusDescription
pendingAccepted but not yet started
processingCurrently being processed
completedFinished successfully
failedFinished with error
cancelledCancelled by client or system

{
"protocol": { "name": "forrst", "version": "0.1.0" },
"id": "req_poll_1",
"call": {
"function": "urn:cline:forrst:ext:async:fn:status",
"version": "1.0.0",
"arguments": {
"operation_id": "op_xyz789"
}
}
}
{
"protocol": { "name": "forrst", "version": "0.1.0" },
"id": "req_poll_1",
"result": {
"operation_id": "op_xyz789",
"status": "processing",
"progress": 0.45,
"started_at": "2024-01-15T10:30:00Z",
"estimated_completion": "2024-01-15T10:31:00Z"
}
}
{
"protocol": { "name": "forrst", "version": "0.1.0" },
"id": "req_poll_2",
"result": {
"operation_id": "op_xyz789",
"status": "completed",
"output": {
"report_url": "https://storage.example.com/reports/annual_2024.pdf",
"generated_at": "2024-01-15T10:31:23Z",
"page_count": 47
}
}
}
{
"protocol": { "name": "forrst", "version": "0.1.0" },
"id": "req_poll_3",
"result": null,
"errors": [{
"code": "ASYNC_OPERATION_FAILED",
"message": "Report generation failed: data source unavailable",
"details": {
"operation_id": "op_xyz789",
"failed_at": "2024-01-15T10:30:45Z",
"reason": "database_connection_timeout"
}
}]
}

Instead of polling, clients MAY provide a callback URL:

{
"protocol": { "name": "forrst", "version": "0.1.0" },
"id": "req_callback",
"call": {
"function": "reports.generate",
"version": "1.0.0",
"arguments": { "type": "annual", "year": 2024 }
},
"extensions": [
{
"urn": "urn:forrst:ext:async",
"options": {
"preferred": true,
"callback_url": "https://my-service.example.com/webhooks/forrst"
}
}
]
}

When the operation completes, the server MUST POST to the callback URL:

{
"protocol": { "name": "forrst", "version": "0.1.0" },
"callback": {
"operation_id": "op_xyz789",
"original_request_id": "req_callback",
"status": "completed",
"result": {
"report_url": "https://storage.example.com/reports/annual_2024.pdf"
},
"completed_at": "2024-01-15T10:31:23Z"
}
}

Servers SHOULD:

  • Sign callbacks with HMAC or similar
  • Include signature in header: X-Forrst-Signature: sha256=...
  • Support callback URL allowlists

Clients SHOULD:

  • Verify callback signatures
  • Validate callback source
  • Respond with 200 to acknowledge

Clients MAY cancel pending operations:

{
"protocol": { "name": "forrst", "version": "0.1.0" },
"id": "req_cancel",
"call": {
"function": "urn:cline:forrst:ext:async:fn:cancel",
"version": "1.0.0",
"arguments": {
"operation_id": "op_xyz789"
}
}
}
{
"protocol": { "name": "forrst", "version": "0.1.0" },
"id": "req_cancel",
"result": {
"operation_id": "op_xyz789",
"status": "cancelled",
"cancelled_at": "2024-01-15T10:30:30Z"
}
}

If the operation has already completed or cannot be cancelled:

{
"protocol": { "name": "forrst", "version": "0.1.0" },
"id": "req_cancel",
"result": null,
"errors": [{
"code": "ASYNC_CANNOT_CANCEL",
"message": "Operation already completed",
"details": {
"operation_id": "op_xyz789",
"status": "completed"
}
}]
}

Servers SHOULD respond asynchronously when:

  • Client specified options.preferred: true
  • Operation would exceed reasonable time (e.g., > 30s)
  • Operation involves external dependencies with high latency

Servers MAY respond synchronously even with preferred: true if:

  • Operation completes quickly
  • Operation is cached
┌─────────┐ ┌────────────┐ ┌───────────┐
│ pending │────▶│ processing │────▶│ completed │
└─────────┘ └────────────┘ └───────────┘
┌──────────┐
│ failed │
└──────────┘

Servers SHOULD:

  • Store operation state durably
  • Set TTL on completed operations (e.g., 24 hours)
  • Return NOT_FOUND for expired operation IDs

Async operations work with the Idempotency extension:

{
"protocol": { "name": "forrst", "version": "0.1.0" },
"id": "req_123",
"call": {
"function": "reports.generate",
"version": "1.0.0",
"arguments": { "type": "annual" }
},
"extensions": [
{
"urn": "urn:forrst:ext:async",
"options": {
"preferred": true
}
},
{
"urn": "urn:forrst:ext:idempotency",
"options": {
"key": "report_annual_2024"
}
}
]
}

If the same idempotency key is used:

  • Servers MUST return existing operation ID if still processing
  • Servers MUST return completed result if finished

1. Initial Request

{
"protocol": { "name": "forrst", "version": "0.1.0" },
"id": "req_report",
"call": {
"function": "reports.generate",
"version": "1.0.0",
"arguments": {
"type": "sales",
"date_range": {
"start": "2024-01-01",
"end": "2024-12-31"
}
}
},
"context": {
"trace_id": "tr_report123",
"span_id": "sp_init"
},
"extensions": [
{
"urn": "urn:forrst:ext:async",
"options": {
"preferred": true
}
}
]
}

2. Immediate Response

{
"protocol": { "name": "forrst", "version": "0.1.0" },
"id": "req_report",
"result": null,
"extensions": [
{
"urn": "urn:forrst:ext:async",
"data": {
"operation_id": "op_sales_report_456",
"status": "processing",
"poll": {
"function": "urn:cline:forrst:ext:async:fn:status",
"version": "1.0.0",
"arguments": { "operation_id": "op_sales_report_456" }
},
"retry_after": { "value": 10, "unit": "second" }
}
}
]
}

3. Poll (Still Processing)

// Request
{
"protocol": { "name": "forrst", "version": "0.1.0" },
"id": "req_poll_1",
"call": {
"function": "urn:cline:forrst:ext:async:fn:status",
"version": "1.0.0",
"arguments": { "operation_id": "op_sales_report_456" }
}
}
// Response
{
"protocol": { "name": "forrst", "version": "0.1.0" },
"id": "req_poll_1",
"result": {
"operation_id": "op_sales_report_456",
"status": "processing",
"progress": 0.67,
"message": "Processing Q3 data..."
}
}

4. Poll (Complete)

{
"protocol": { "name": "forrst", "version": "0.1.0" },
"id": "req_poll_2",
"result": {
"operation_id": "op_sales_report_456",
"status": "completed",
"output": {
"report_id": "rpt_789",
"download_url": "https://...",
"expires_at": "2024-01-16T10:30:00Z",
"summary": {
"total_sales": 1250000,
"record_count": 45000
}
}
}
}

The async extension provides these functions for operation management. They use the reserved forrst. namespace but are only available when the async extension is enabled.

Check status of an async operation.

Request:

{
"protocol": { "name": "forrst", "version": "0.1.0" },
"id": "req_op_status",
"call": {
"function": "urn:cline:forrst:ext:async:fn:status",
"version": "1.0.0",
"arguments": {
"operation_id": "op_xyz789"
}
}
}

Arguments:

FieldTypeRequiredDescription
operation_idstringYesOperation ID to check

Returns:

FieldTypeDescription
operation_idstringOperation identifier
functionstringFunction that was invoked
versionstringFunction version
statusstringCurrent status (see Status Values)
progressnumberCompletion percentage (0.0 to 1.0)
resultanyFunction result (when completed)
errorsarrayError details (when failed)
started_atstringISO 8601 timestamp when processing started
completed_atstringISO 8601 timestamp when finished

Errors:

CodeDescription
ASYNC_OPERATION_NOT_FOUNDOperation ID does not exist or has expired

Cancel a pending or processing async operation.

Request:

{
"protocol": { "name": "forrst", "version": "0.1.0" },
"id": "req_op_cancel",
"call": {
"function": "urn:cline:forrst:ext:async:fn:cancel",
"version": "1.0.0",
"arguments": {
"operation_id": "op_xyz789"
}
}
}

Arguments:

FieldTypeRequiredDescription
operation_idstringYesOperation ID to cancel

Returns:

FieldTypeDescription
operation_idstringOperation ID
statusstringNew status (MUST be cancelled)
cancelled_atstringISO 8601 timestamp

Errors:

CodeDescription
ASYNC_OPERATION_NOT_FOUNDOperation ID does not exist
ASYNC_CANNOT_CANCELOperation already completed or cannot be cancelled

List operations for the current caller.

Request:

{
"protocol": { "name": "forrst", "version": "0.1.0" },
"id": "req_op_list",
"call": {
"function": "urn:cline:forrst:ext:async:fn:list",
"version": "1.0.0",
"arguments": {
"status": "processing",
"limit": 10
}
}
}

Arguments:

FieldTypeRequiredDescription
statusstringNoFilter by status
functionstringNoFilter by function name
limitintegerNoMax results (default 50)
cursorstringNoPagination cursor

Returns:

{
"operations": [
{
"id": "op_abc123",
"function": "reports.generate",
"version": "1.0.0",
"status": "processing",
"progress": 0.45,
"started_at": "2024-01-15T10:30:00Z"
}
],
"next_cursor": "eyJpZCI6Im9wX2RlZjQ1NiJ9"
}
FieldTypeDescription
operationsarrayArray of operation summaries
next_cursorstringCursor for next page (null if no more)