# Quickstart Guide

This guide walks you through creating your first MFP agents, establishing a secure channel, and exchanging messages.

## Prerequisites

- Python 3.11+
- MFP installed: `pip install mfp`

## Step 1: Create a Runtime

Every MFP application starts with a `Runtime`:

```python
from mfp import Runtime, RuntimeConfig

# Create runtime configuration
config = RuntimeConfig()

# Initialize the runtime
runtime = Runtime(config)
```

The `Runtime` manages agent lifecycles, message routing, and cryptographic operations.

## Step 2: Define Agent Callables

Agents are Python callables that receive and process messages:

```python
def alice(msg):
    """Alice's message handler."""
    print(f"[Alice] Received: {msg.payload.decode()}")

def bob(msg):
    """Bob's message handler."""
    print(f"[Bob] Received: {msg.payload.decode()}")
```

**Signature:** `(msg: DeliveredMessage) -> None` (sync) or `async (msg: DeliveredMessage) -> None` (async)

The agent callable receives a `DeliveredMessage` with `payload`, `sender`, `channel`, and `message_id` fields. Async callables are auto-detected by `bind()`.

## Step 3: Bind Agents to the Runtime

Binding registers an agent with the runtime and assigns it a unique `AgentId`:

```python
from mfp import bind

# Bind agents (accepts both sync and async callables)
alice_handle = bind(runtime, alice)
bob_handle = bind(runtime, bob)

print(f"Alice ID: {alice_handle.agent_id.value.hex()[:16]}...")
print(f"Bob ID: {bob_handle.agent_id.value.hex()[:16]}...")
```

**AgentHandle** provides the agent-facing interface: sending messages, listing channels, querying status.

## Step 4: Establish a Channel

Create a channel between Alice and Bob. This is a Layer 2 (admin) operation on the Runtime:

```python
# Admin establishes a channel between Alice and Bob
channel_id = runtime.establish_channel(alice_handle.agent_id, bob_handle.agent_id)

print("Channel established!")
```

The Runtime derives a unique ratchet seed for the channel using AES-256-GCM encryption.

## Step 5: Send Messages

Use the `mfp_send` tool to send encrypted messages:

```python
from mfp import mfp_send, mfp_channels

# Get Alice's channels
channels = mfp_channels(alice_handle)
channel = channels[0]

print(f"Channel ID: {channel.channel_id.value.hex()[:16]}...")
print(f"Peer: {channel.peer.value.hex()[:16]}...")

# Alice sends to Bob
receipt = mfp_send(
    alice_handle,
    channel_id=channel.channel_id,
    payload=b"Hello Bob, this is Alice!",
)

print(f"Message sent! Step: {receipt.step}")
```

**Receipt** contains:
- `message_id`: unique message identifier
- `channel`: the channel used
- `step`: the ratchet step at send time

## Step 6: Query Agent Status

Check agent state at any time:

```python
from mfp import mfp_status

# Get Alice's status
status = mfp_status(alice_handle)

print(f"Agent state: {status.state}")  # AgentStatus.ACTIVE
print(f"Channels: {status.channel_count}")
print(f"Pending messages: {status.pending_message_count}")
```

**AgentStatus** states:
- `BOUND` — registered, no channels yet
- `ACTIVE` — has at least one channel
- `QUARANTINED` — isolated due to errors
- `UNBOUND` — deregistered

## Step 7: Two-Way Communication

Bob can send back to Alice on the same channel:

```python
# Bob gets his channels (same channel, from his perspective)
bob_channels = mfp_channels(bob_handle)
bob_channel = bob_channels[0]

# Bob replies
reply_receipt = mfp_send(
    bob_handle,
    channel_id=bob_channel.channel_id,
    payload=b"Hi Alice! Message received.",
)

print(f"Reply sent: {reply_receipt.message_id.hex()[:16]}...")
```

Both Alice and Bob share the same `ChannelId`, ensuring symmetric communication.

## Step 8: Shutdown

Clean up when done:

```python
from mfp import unbind

# Unbind agents (closes channels, flushes state)
unbind(alice_handle)
unbind(bob_handle)

# Shutdown the runtime
runtime.shutdown()

print("Runtime shut down cleanly.")
```

## Complete Example

```python
from mfp import (
    Runtime, RuntimeConfig,
    bind, unbind,
    mfp_send, mfp_channels, mfp_status
)

# Agent callables
def alice(msg):
    print(f"[Alice] {msg.payload.decode()}")

def bob(msg):
    print(f"[Bob] {msg.payload.decode()}")

# Initialize
config = RuntimeConfig()
runtime = Runtime(config)

# Bind
alice_handle = bind(runtime, alice)
bob_handle = bind(runtime, bob)

# Establish channel (Layer 2 / admin operation)
channel_id = runtime.establish_channel(alice_handle.agent_id, bob_handle.agent_id)

# Send message
receipt = mfp_send(
    alice_handle,
    channel_id=channel_id,
    payload=b"Hello from Alice!",
)

print(f"Sent at step: {receipt.step}")

# Cleanup
unbind(alice_handle)
unbind(bob_handle)
runtime.shutdown()
```

## Next Steps

- **[API Reference](api-reference.md)** — explore the full library interface
- **[Architecture](architecture.md)** — understand the internal design
- **[Security Model](security.md)** — learn about MFP's security guarantees
- **[Production Guide](production-guide.md)** — deployment and monitoring
- **[Migration Guide](migration-guide.md)** — upgrading from 0.1.0

---

**Tip:** For production use, persist agent state using `StorageConfig` and configure federation via `TransportServer`. See the [Production Guide](production-guide.md) for details.
