API Reference¶
Desktop Agent¶
fdc3.desktop_agent.server
¶
FDC3 Desktop Agent server package.
This package provides the FastAPI application plus the WebSocket entrypoint that implements WCP/DACP messaging.
Most users will interact with:
create_appto embed the agent in another Python process.appfor the default application instance.
Submodules
app_factory: FastAPI application factory.websocket: WebSocket endpoint handler.routes: HTTP route handlers.lifespan: Application lifecycle management (startup/shutdown).
AgentClientConnectionManager
¶
BridgeClient
¶
BridgeClient(settings: BridgeConnectionSettings, *, implementation_metadata_factory: ImplementationMetadataFactory, channels_state_factory: ChannelsStateFactory, request_handler: RequestHandler, connected_agents_update_handler: Optional[Callable[[BridgeConnectedAgentsUpdatePayload], Awaitable[None]]] = None, connect_func: Optional[ConnectFunc] = None)
Desktop Agent Bridge client implementing BCP/BMP.
This is intentionally minimal: it supports - discovery + connect over the recommended port range (4475-4575) - BCP: hello -> handshake -> connectedAgentsUpdate - BMP: send request + await response by requestUuid - receive bridge-forwarded requests and respond via an injected handler
The bridge is expected to validate/augment meta.source.desktopAgent.
assigned_name
property
¶
Name assigned by the bridge during the handshake.
has_connected_agent
¶
Return True if the named desktop agent is currently connected.
stop
async
¶
Stop the bridge client and close resources.
Raises:
| Type | Description |
|---|---|
Exception
|
Re-raises errors from background tasks when shutting down. |
send_agent_request
async
¶
send_agent_request(*, request_type: str, payload: dict[str, Any], source: AppIdentifierLike, destination: Optional[AppIdentifierLike] = None, timeout: Optional[float] = None) -> BridgeResponseDict
Send an agentRequest message and await the (bridge-collated) response.
send_request_no_wait
async
¶
send_request_no_wait(*, request_type: str, payload: dict[str, Any], source: AppIdentifierLike, destination: Optional[AppIdentifierLike] = None) -> str
Send a request message that does not generate a response.
Used for BMP 'fire and forget' messages such as broadcastRequest.
Returns the generated requestUuid.
AccessControlHandler
¶
Handles WebSocket access control validation
validate_connection
async
¶
Validate WebSocket connection based on access control policy.
Returns True if connection is allowed, False otherwise. Closes the WebSocket connection if access is denied.
WCPHandler
¶
Handles WCP (Web Connection Protocol) messages
handle_message
async
¶
handle_message(message: Dict[str, Any], session_id: str, wcp_sessions: WcpSessions, sender: MessageSender) -> Optional[str]
Handle WCP message and return next phase if transition occurs.
Returns:
| Type | Description |
|---|---|
Optional[str]
|
"dacp" if transitioning to DACP phase |
Optional[str]
|
None if staying in WCP phase |
DACPHandler
¶
DACPHandler(storage: Storage, launcher: ProcessLauncher, connection_manager: WebSocketConnectionManager, web_launcher: Optional[WebEndpointLauncher] = None, core=None)
Bases: AppHandlersMixin, ChannelHandlersMixin, EventHandlersMixin, IntentHandlersMixin
Handles DACP (Desktop Agent Communication Protocol) messages
handle_message
async
¶
handle_message(message: Dict[str, Any], session_id: str, wcp_sessions: WcpSessions, sender: MessageSender)
Handle DACP message with centralized Pydantic validation.
WebSocketConnectionManager
¶
create_app
¶
create_app(config: Optional[DesktopAgentConfig] = None) -> FastAPI
Create and configure the FDC3 Desktop Agent FastAPI application.
This factory function allows embedding the agent in another Python project with full control over configuration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
Optional[DesktopAgentConfig]
|
Optional configuration. If None, uses environment variables and defaults. |
None
|
Returns:
| Type | Description |
|---|---|
FastAPI
|
A configured FastAPI application ready to be run with uvicorn or |
FastAPI
|
mounted into a larger ASGI application. |
fdc3.desktop_agent.cli
¶
Command-line stub to launch the FDC3 Desktop Agent.
Invokes uvicorn (or uv run uvicorn) in a subprocess and forwards Ctrl-C / SIGTERM so shutdown logs complete before the shell prompt returns.
main
¶
Launch the desktop agent or manage the app directory.
If no arguments are provided, starts uvicorn in dev mode with reload.
fdc3.desktop_agent.config
¶
Configuration for the FDC3 Desktop Agent.
This module provides the DesktopAgentConfig dataclass used to configure
the agent when embedding it in another Python application.
DesktopAgentConfig
dataclass
¶
DesktopAgentConfig(host: str = (lambda: getenv('FDC3_HOST', 'localhost'))(), port: int = (lambda: int(getenv('FDC3_PORT', '8000')))(), db_path: str = (lambda: getenv('FDC3_DB_PATH', 'fdc3_agent.db'))(), log_level: str = (lambda: getenv('FDC3_LOG_LEVEL', 'INFO'))(), allowed_origins: List[str] = _default_allowed_origins(), storage: Optional['Storage'] = None, launcher: Optional['ProcessLauncher'] = None, web_launcher: Optional['WebEndpointLauncher'] = None, distributed_adapter: Optional['DistributedLogAdapter'] = None, plugins: List['IntentHandlerPlugin'] = list(), auto_discover_plugins: bool = (lambda: lower() in ('true', '1', 'yes'))(), agent_url: Optional[str] = None, bridge_enabled: bool = (lambda: lower() in ('true', '1', 'yes'))(), bridge_host: str = (lambda: getenv('FDC3_BRIDGE_HOST', '127.0.0.1'))(), bridge_port_start: int = (lambda: int(getenv('FDC3_BRIDGE_PORT_START', '4475')))(), bridge_port_end: int = (lambda: int(getenv('FDC3_BRIDGE_PORT_END', '4575')))(), bridge_requested_name: str = (lambda: getenv('FDC3_BRIDGE_REQUESTED_NAME', gethostname()))(), bridge_connect_retry_seconds: float = (lambda: float(getenv('FDC3_BRIDGE_CONNECT_RETRY_SECONDS', '5')))(), bridge_request_timeout_seconds: float = (lambda: float(getenv('FDC3_BRIDGE_REQUEST_TIMEOUT_SECONDS', '3')))())
Configuration for the FDC3 Desktop Agent.
All fields have sensible defaults, allowing you to create a minimal
configuration with just DesktopAgentConfig().
When embedding the agent, you can override any field::
config = DesktopAgentConfig(
host="0.0.0.0",
port=9000,
db_path=":memory:",
)
app = create_app(config)
You can also inject custom implementations of storage, launcher, or distributed adapter::
config = DesktopAgentConfig(
storage=MyCustomStorage(),
launcher=MyCustomLauncher(),
)
Examples¶
Start the agent quickly with Docker:
See getting-started.md for the full walkthrough.
Client¶
fdc3.client.client
¶
Async client for connecting external intent handlers to the desktop agent.
The primary entry point is FDC3Client, which
implements the WebSocket Connection Protocol (WCP) handshake and provides
helpers for registering an external handler, subscribing to context/intent
notifications, and sending results.
Typical usage:
```python
import asyncio
from fdc3.client.client import FDC3Client
async def main() -> None:
async with FDC3Client("ws://localhost:8000/ws", handler_id="my-handler") as c:
await c.register_handler("my-handler", intents=["ViewChart"])
async def on_intent(msg):
# msg is a validated Pydantic model for known message types.
await c.send_intent_result(msg.meta["requestUuid"], result={"type": "fdc3.nothing"})
c.forwarded_intent_handlers.add(on_intent)
await c.run_forever()
asyncio.run(main())
```
DACPRequest
¶
Bases: Protocol
Protocol for DACP request models with meta attribute.
FDC3Client
¶
Client for external intent handlers to connect to the FDC3 desktop agent.
The client is designed for external intent handler processes that need to:
- establish a WebSocket connection to an agent;
- complete the WCP handshake;
- register/unregister an external handler and supported intents;
- receive forwarded intents and broadcasts via
EventEmitter.
Notes
- This is an asyncio-based client.
- Message handlers registered on the public emitters receive validated Pydantic models for known message types.
send_dacp_request
async
¶
send_dacp_request(request: DACPRequest, timeout: float = 5.0) -> Any
Send a DACP request model and wait for the correlated response.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
DACPRequest
|
A Pydantic DACP request model (e.g., generated from the
|
required |
timeout
|
float
|
Seconds to wait for the correlated response. |
5.0
|
Returns:
| Type | Description |
|---|---|
Any
|
The parsed response payload (model or raw dict) returned by the agent. |
Raises:
| Type | Description |
|---|---|
TimeoutError
|
If a response is not received within |
RuntimeError
|
If the client is not connected or handshake not complete. |
connect
async
¶
Connect to the agent and complete the WCP handshake.
After this returns successfully, wait_for_handshake should be
satisfied and request/response helper methods (e.g. register_handler) can be used.
Raises:
| Type | Description |
|---|---|
Exception
|
If the websocket connection or handshake fails. |
wait_for_handshake
async
¶
Wait for WCP handshake to complete.
Returns:
| Type | Description |
|---|---|
bool
|
True if handshake succeeded, False if timed out. |
register_handler
async
¶
register_handler(handler_id: str, intents: List[str], *, priority: int = 0, metadata: Optional[Dict[str, Any]] = None, timeout: float = 5.0) -> str
Register an external handler and return the agent-assigned handler UUID.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
handler_id
|
str
|
A stable identifier for this handler. |
required |
intents
|
List[str]
|
Intent names the handler can service. |
required |
priority
|
int
|
Higher values may win in resolver selection (agent-dependent). |
0
|
metadata
|
Optional[Dict[str, Any]]
|
Arbitrary handler metadata for discovery/UI. |
None
|
timeout
|
float
|
Seconds to wait for the correlated response. |
5.0
|
Returns:
| Type | Description |
|---|---|
str
|
The agent-assigned handler UUID. |
See also
Use :attr:forwarded_intent_handlers to receive forwarded intents.
add_context_listener
async
¶
Register a context listener and return the listener UUID.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
context_type
|
Optional[str]
|
If provided, only contexts of this FDC3 type are delivered. |
None
|
timeout
|
float
|
Seconds to wait for the correlated response. |
5.0
|
Returns:
| Type | Description |
|---|---|
str
|
Listener UUID that can be passed to |
remove_context_listener
async
¶
Unsubscribe a previously-registered context listener.
add_intent_listener
async
¶
Register an intent listener and return its listener UUID.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
intent
|
str
|
The intent name to listen for (e.g., "ViewChart"). |
required |
timeout
|
float
|
Seconds to wait for the agent's response. |
5.0
|
Returns:
| Type | Description |
|---|---|
str
|
Listener UUID that can be passed to |
remove_intent_listener
async
¶
Unsubscribe a previously-registered intent listener.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
listener_uuid
|
str
|
The UUID returned from |
required |
timeout
|
float
|
Seconds to wait for the agent's response. |
5.0
|
unregister_handler
async
¶
Unregister a handler by its UUID.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
handler_uuid
|
str
|
The UUID returned from |
required |
timeout
|
float
|
Seconds to wait for the agent's response. |
5.0
|
send_intent_result
async
¶
send_intent_result(request_uuid: str, *, result: Optional[Dict[str, Any]] = None, error: Optional[str] = None) -> None
Send a result for a previously forwarded intent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request_uuid
|
str
|
The request UUID from the forwarded intent message meta. |
required |
result
|
Optional[Dict[str, Any]]
|
Optional result payload. |
None
|
error
|
Optional[str]
|
Optional error string. |
None
|
Notes
Many handler processes call this from a callback registered via
:attr:forwarded_intent_handlers.
emit_channel_event
async
¶
emit_channel_event(event_type: str, channel_id: str, *, instance_uuid: Optional[str] = None, context: Optional[Dict[str, Any]] = None) -> None
Emit a dev-only channel event via the server GraphQL emitChannelEvent mutation.
This is intended for examples and demos only.
create_user_channel
async
¶
Create a user channel via the agent GraphQL API.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
channel_id
|
str
|
The desired channel identifier (e.g., "demo" or "user:demo"). |
required |
display_metadata
|
Optional[DisplayMetadata]
|
Optional |
None
|
Raises:
| Type | Description |
|---|---|
HTTPError
|
If the GraphQL request fails. |
join_user_channel
async
¶
Join a user channel, optionally auto-creating it if missing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
channel_id
|
str
|
The channel identifier (e.g., "demo" or "user:demo"). |
required |
auto_create
|
bool
|
If True and the channel doesn't exist, create it first. |
False
|
Returns:
| Type | Description |
|---|---|
JoinUserChannelResponse
|
The JoinUserChannelResponse containing the joined channel details. |
Raises:
| Type | Description |
|---|---|
Exception
|
If the channel doesn't exist and auto_create is False. |
leave_current_channel
async
¶
Leave the currently joined channel.
This sends a DACP leaveCurrentChannel request to the agent.
Raises:
| Type | Description |
|---|---|
Exception
|
If the agent returns an error or the request fails. |
create_private_channel
async
¶
Create a private channel and return the response.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
display_name
|
Optional[str]
|
Optional human-readable name for the channel. |
None
|
Returns:
| Type | Description |
|---|---|
CreatePrivateChannelResponse
|
The CreatePrivateChannelResponse containing the new channel's metadata. |
create_private_channel_invite
async
¶
create_private_channel_invite(channel_id: str, instance_id: Optional[str] = None) -> CreatePrivateChannelInvitationResponse
Create a private channel invitation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
channel_id
|
str
|
The private channel to create an invitation for. |
required |
instance_id
|
Optional[str]
|
Optional target instance to restrict the invitation to. |
None
|
Returns:
| Type | Description |
|---|---|
CreatePrivateChannelInvitationResponse
|
The CreatePrivateChannelInvitationResponse containing the invitation token. |
join_private_channel
async
¶
Join a private channel using an invitation token.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
channel_id
|
str
|
The private channel to join. |
required |
token
|
str
|
The invitation token received from the channel creator. |
required |
Returns:
| Type | Description |
|---|---|
JoinPrivateChannelResponse
|
The JoinPrivateChannelResponse confirming the join. |
leave_private_channel
async
¶
Leave a private channel.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
channel_id
|
str
|
The private channel to leave. |
required |
add_private_channel_event_listener
async
¶
add_private_channel_event_listener(channel_id: str, *, event_type: Optional[PrivateChannelEventListenerTypes] = None) -> PrivateChannelAddEventListenerResponse
Subscribe to private channel events for a channel.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
channel_id
|
str
|
The private channel to subscribe to. |
required |
event_type
|
Optional[PrivateChannelEventListenerTypes]
|
Optionally filter to a specific event type. |
None
|
Returns:
| Type | Description |
|---|---|
PrivateChannelAddEventListenerResponse
|
The PrivateChannelAddEventListenerResponse containing listener details. |
build_message
async
¶
Build a minimal fdc3.message payload for a chat message.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
The plain text content of the message. |
required |
Returns:
| Type | Description |
|---|---|
MessageContext
|
A |
get_chat_room
async
¶
get_chat_room(channel_id: str, *, provider_name: Optional[str] = None, auto_create: bool = False) -> ChatRoomContext
Return a fdc3.chat.room object for a given channel id.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
channel_id
|
str
|
The channel identifier to build a chat room for. |
required |
provider_name
|
Optional[str]
|
Optional provider name to include in the room. |
None
|
auto_create
|
bool
|
If True, attempt to create a user channel on the agent (best-effort) before returning the room object. |
False
|
Returns:
| Type | Description |
|---|---|
ChatRoomContext
|
A |
ChatRoomContext
|
|
send_chat_message
async
¶
send_chat_message(text: str, channel_id: str, *, provider_name: Optional[str] = None, auto_create_room: bool = False) -> None
Send a fdc3.chat.message to the agent by broadcasting the
appropriate context object.
This constructs the chatRoom and message objects and calls
broadcast with the resulting context.
broadcast
async
¶
Send a DACP broadcast request to the agent to broadcast context.
This will cause the agent to deliver the context to the channel the sending instance is currently joined to.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
context
|
Any
|
An FDC3 context object (dict or Pydantic model) to broadcast. |
required |
Example
await client.broadcast({"type": "fdc3.instrument", "id": {"ticker": "AAPL"}})
run_forever
async
¶
Block until the connection closes, then clean up.
This is a convenience for long-running external handler processes.
Examples¶
Register an external handler and run the client:
import asyncio
from fdc3.client.client import FDC3Client
async def main() -> None:
async with FDC3Client("ws://localhost:8000/ws", handler_id="my-handler") as c:
await c.register_handler("my-handler", intents=["ViewChart"])
await c.run_forever()
asyncio.run(main())
See getting-started.md for a minimal demo client.
Models¶
fdc3.models.identifiers
¶
Identifier and small domain types.
This module contains compact Pydantic models used across the codebase
and was previously re-exported from the generated
fdc3.desktop_agent.api. Keeping the definitions here avoids import
cycles and provides a stable surface for other modules.