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

# AgentContext

> Execution context passed to agent handlers, providing access to environment, capabilities, and metadata.

## Definition

```python theme={null}
@dataclass
class AgentContext:
    env: dict[str, str] = field(default_factory=dict)
    config: dict = field(default_factory=dict)
    session: SessionData | None = None
    output_schema: dict | None = None
    tools: Tools = field(default_factory=_uninitialized_tools)
    llm: Llm = field(default_factory=_uninitialized_llm)
    http: Http = field(default_factory=_uninitialized_http)
    stream: StreamEmitter = field(default_factory=_uninitialized_stream)
```

Capability fields (`tools`, `llm`, `http`, `stream`) are always non-None. They default to safe stubs that raise `RuntimeError` if called outside the host environment.

## Fields

### `env`

* **Type:** `dict[str, str]`
* **Description:** Environment variables configured via the `@agent` decorator's `environment` field.

```python theme={null}
api_key = ctx.env["ANTHROPIC_API_KEY"]  # Raises KeyError if not set
debug = ctx.env.get("DEBUG", "false")   # Safe access with default
```

Populated from Friday's environment matching `environment.required` and `environment.optional` configuration.

### `config`

* **Type:** `dict`
* **Description:** Agent-specific configuration and space context.

May include:

* `platformUrl` - Friday API base URL
* `skills` - List of space skills for the session
* `workDir` - Existing space directory (if FSM set one up)
* Custom fields passed by the orchestrator

```python theme={null}
platform_url = ctx.config.get("platformUrl", "http://localhost:18080")
skills = ctx.config.get("skills", [])
```

### `session`

* **Type:** `SessionData | None`
* **Description:** Session metadata when running within a Friday session.

```python theme={null}
@dataclass
class SessionData:
    id: str            # Session identifier
    workspace_id: str  # Space identifier
    user_id: str       # User identifier
    datetime: str      # ISO format timestamp
```

May be `None` in test contexts or standalone execution.

### `output_schema`

* **Type:** `dict | None`
* **Description:** JSON Schema for structured output, if specified by the caller.

```python theme={null}
if ctx.output_schema:
    # Use structured generation
    result = ctx.llm.generate_object(..., schema=ctx.output_schema)
else:
    # Standard text generation
    result = ctx.llm.generate(...)
```

### `tools`

* **Type:** `Tools`
* **Description:** MCP tool capability wrapper. Always available - returns empty list when no MCP servers configured.

Methods:

* `ctx.tools.list()` → `list[ToolDefinition]`
* `ctx.tools.call(name, args)` → `dict`

See [ctx.tools](/sdk/python-reference/tools-capability).

### `llm`

* **Type:** `Llm`
* **Description:** LLM capability wrapper for generation calls. Always available in host environment.

Methods:

* `ctx.llm.generate(messages, model, ...)` → `LlmResponse`
* `ctx.llm.generate_object(messages, schema, ...)` → `LlmResponse`

See [ctx.llm](/sdk/python-reference/llm-capability).

### `http`

* **Type:** `Http`
* **Description:** HTTP capability wrapper for outbound requests. Always available in host environment.

Methods:

* `ctx.http.fetch(url, method, headers, body, timeout_ms)` → `HttpResponse`

See [ctx.http](/sdk/python-reference/http-capability).

### `stream`

* **Type:** `StreamEmitter`
* **Description:** Stream capability for progress emission. Always available in host environment.

Methods:

* `ctx.stream.progress(content, tool_name)`
* `ctx.stream.intent(content)`
* `ctx.stream.emit(event_type, data)`

See [ctx.stream](/sdk/python-reference/stream-capability).

## Availability Guarantees

| Field           | Guaranteed | Notes                                                  |
| --------------- | ---------- | ------------------------------------------------------ |
| `env`           | Yes        | Empty dict if no environment configured                |
| `config`        | Yes        | Empty dict if no config provided                       |
| `session`       | No         | May be None outside Friday sessions                    |
| `output_schema` | No         | Only when caller specifies schema                      |
| `tools`         | Yes        | Always present; returns empty list if no MCP servers   |
| `llm`           | Yes        | Always present; raises RuntimeError if not initialized |
| `http`          | Yes        | Always present; raises RuntimeError if not initialized |
| `stream`        | Yes        | Always present; safe no-op in test contexts            |

## Defensive Programming

```python theme={null}
from friday_agent_sdk import agent, ok

@agent(id="safe", version="1.0.0", description="Handles missing capabilities")
def execute(prompt, ctx):
    # Safe access with fallbacks
    api_key = ctx.env.get("OPTIONAL_KEY")  # Returns None if missing

    # Required access with check
    if "REQUIRED_KEY" not in ctx.env:
        return err("REQUIRED_KEY not set. Connect in Friday Link.")

    # Capability availability - capabilities are always present
    # They raise RuntimeError if called outside the host environment
    if ctx.output_schema:
        # Structured path
        result = ctx.llm.generate_object(...)
    else:
        # Standard path
        result = ctx.llm.generate(...)

    # Progress emission - always safe to call
    ctx.stream.progress("Working...")

    return ok({"result": result.text})
```

**Test contexts:** When running outside Friday (unit tests), calling capabilities raises `RuntimeError` with a clear message. To test agents properly, mock the capabilities or run against a local Friday daemon.

## Context Round-Trip

All context fields are serialized over NATS for each invocation:

1. Friday serializes context as JSON
2. JSON is sent to the agent subprocess via NATS
3. SDK bridge deserializes to `AgentContext` dataclass
4. Your code uses the context
5. Result serializes back to the host

The `context-inspector` example agent demonstrates all fields survive this round-trip correctly.

## See Also

<CardGroup cols={2}>
  <Card title="ctx.llm" icon="microchip" href="/sdk/python-reference/llm-capability">
    LLM generation
  </Card>

  <Card title="ctx.http" icon="globe" href="/sdk/python-reference/http-capability">
    HTTP requests
  </Card>

  <Card title="ctx.tools" icon="wrench" href="/sdk/python-reference/tools-capability">
    MCP tool calls
  </Card>

  <Card title="ctx.stream" icon="bars-progress" href="/sdk/python-reference/stream-capability">
    Progress streaming
  </Card>

  <Card title="Result Types" icon="code" href="/sdk/python-reference/result-types">
    ok() and err() return values
  </Card>
</CardGroup>
