Messaging connectors giving AI agents authenticated, normalized access to email and chat platforms
Project description
appif -- Application Interfaces
A Python library that gives AI agents authenticated, normalized access to external platforms -- email, chat, and work tracking systems.
Purpose
Agents need information that lives behind logins: email threads, Slack messages, Jira tickets. This library provides connectors and adapters that authenticate as you and return clean, structured domain objects suitable for agent reasoning -- platform-specific APIs are fully encapsulated behind shared protocols.
Two domains are supported:
- Messaging -- Gmail, Outlook, Slack. Unified
MessageEventobjects via theConnectorprotocol. - Work Tracking -- Jira. Unified
WorkItemobjects via theWorkTrackerprotocol. Multi-instance support with YAML config.
For the complete usage guide -- the unified model, per-connector mapping tables, code examples, and environment variable reference -- see docs/usage.md.
Quick Start
Messaging (Gmail, Outlook, Slack)
pip install appif
from appif.adapters.gmail import GmailConnector
from appif.domain.messaging.models import MessageEvent, MessageContent
class MyListener:
def on_message(self, event: MessageEvent) -> None:
print(f"[{event.connector}] {event.author.display_name}: {event.content.text}")
connector = GmailConnector()
connector.connect()
connector.register_listener(MyListener())
All three messaging connectors (Gmail, Outlook, Slack) follow this same pattern. The full model, per-connector setup, and examples are in docs/usage.md.
Work Tracking (Jira)
from appif.domain.work_tracking.service import WorkTrackingService
from appif.domain.work_tracking.models import CreateItemRequest, ItemCategory, SearchCriteria
service = WorkTrackingService() # Loads from ~/.config/appif/jira/config.yaml
# Create a ticket using domain-level categories (adapter resolves to platform type)
item = service.create_item(CreateItemRequest(
project="MYPROJECT",
title="Fix login bug",
item_type=ItemCategory.BUG,
description="Users cannot log in after password reset",
))
print(f"Created: {item.key}")
# Search
results = service.search(SearchCriteria(project="MYPROJECT", status="To Do"))
for item in results.items:
print(f" {item.key}: {item.title}")
# List projects
for project in service.list_projects():
print(f" {project.key}: {project.name} ({project.project_type})")
# Get project details
project = service.get_project("MYPROJECT")
print(f"Lead: {project.lead.display_name if project.lead else 'unassigned'}")
See docs/design/work_tracking/setup.md for configuration.
Supported Platforms
Messaging Connectors
| Service | Connector | Inbound Method | Status |
|---|---|---|---|
| Gmail | Google API (OAuth 2.0) | history.list polling |
Active |
| Outlook / Microsoft 365 | Microsoft Graph API | Delta-query polling | Active |
| Slack | Slack API (Bolt + Socket Mode) | Real-time Socket Mode | Active |
Work Tracking Adapters
| Service | Library | Auth Method | Status |
|---|---|---|---|
| Jira Cloud | atlassian-python-api |
API token (YAML config) | Active |
CLI
The Slack adapter includes a command-line interface:
# Install makes the CLI available
pip install appif
# List channels, send messages, monitor in real-time
appif-slack channels
appif-slack send --channel general "Hello from CLI"
appif-slack monitor --channel general
Installation
For development
uv venv .venv
source .venv/bin/activate
uv pip install -e ".[dev]"
As a library dependency
pip install appif
Prerequisites
- Python 3.13.x
- uv (for development)
Configuration
Messaging (environment variables)
Credentials are stored in ~/.env and loaded at runtime:
cp .env.example ~/.env
| Variable | Service | Required |
|---|---|---|
APPIF_GMAIL_CLIENT_ID |
Gmail | Yes -- Google Cloud OAuth client ID |
APPIF_GMAIL_CLIENT_SECRET |
Gmail | Yes -- Google Cloud OAuth client secret |
APPIF_GMAIL_ACCOUNT |
Gmail | Yes -- Account email address |
APPIF_OUTLOOK_CLIENT_ID |
Outlook | Yes -- Azure AD app (client) ID |
APPIF_OUTLOOK_TENANT_ID |
Outlook | Optional -- Azure AD tenant (default: common) |
APPIF_SLACK_BOT_OAUTH_TOKEN |
Slack | Yes -- Bot user OAuth token (xoxb-...) |
APPIF_SLACK_BOT_APP_LEVEL_TOKEN |
Slack | Yes -- App-level token for Socket Mode (xapp-...) |
See .env.example for the full template with all optional variables.
Work Tracking (YAML config)
Jira uses a YAML config file at ~/.config/appif/jira/config.yaml:
instances:
personal:
jira:
url: https://your-domain.atlassian.net
username: your-email@example.com
api_token: your-api-token
default: personal
Override the config path with APPIF_JIRA_CONFIG env var. Multiple instances supported.
See docs/design/work_tracking/setup.md for the full setup guide.
Project Structure
application_interfaces/
├── src/
│ └── appif/ # Top-level package (PyPI: appif)
│ ├── __init__.py # Version via importlib.metadata
│ ├── domain/
│ │ ├── messaging/ # Connector protocol, canonical models, errors
│ │ └── work_tracking/ # WorkTracker protocol, models, service
│ ├── adapters/
│ │ ├── gmail/ # Gmail messaging connector
│ │ ├── outlook/ # Outlook messaging connector
│ │ ├── slack/ # Slack messaging connector
│ │ └── jira/ # Jira work tracking adapter
│ ├── cli/ # CLI entry points (Slack)
│ └── infrastructure/ # Credential loading
├── tests/
│ ├── unit/ # 298 unit tests
│ ├── integration/ # Live API tests (Slack, Jira)
│ └── e2e/
├── scripts/ # OAuth consent flows, cleanup utilities
├── docs/design/ # Design documents per adapter
├── pyproject.toml
├── ADAPTERS.md # Detailed adapter documentation
├── .env.example
└── readme.md
Development
# Set up dev environment
uv venv .venv
source .venv/bin/activate
uv pip install -e ".[dev]"
# Run all unit tests (298 tests)
pytest tests/unit -v
# Run adapter-specific tests
pytest tests/unit/test_gmail_*.py -v
pytest tests/unit/test_outlook_*.py -v
# Run integration tests (requires live credentials)
pytest tests/integration/test_jira_integration.py -v
pytest tests/integration/test_slack_integration.py -v
# Clean up Jira test tickets
python scripts/jira_cleanup.py
# Lint and format
ruff check src/ tests/
ruff format src/ tests/
# Type check
mypy src/
Architecture
Messaging: Connector Protocol
All messaging connectors implement a shared Connector protocol (appif.domain.messaging.ports.Connector) -- a transport adapter that:
- Connects to an external system and manages authentication
- Emits normalized
MessageEventobjects to registered listeners - Delivers outbound messages via
send(target, content) - Supports historical backfill alongside realtime event ingestion
- Advertises capabilities so upstream logic branches on what the connector supports, not which platform it is
All connectors produce identical canonical types (MessageEvent, ConversationRef, SendReceipt). Platform-specific SDK code is fully encapsulated -- zero Slack/Outlook/Gmail types leak through the public interface.
Work Tracking: WorkTracker Protocol
The Jira adapter implements the WorkTracker protocol (appif.domain.work_tracking.ports.WorkTracker):
- CRUD operations: get, create, comment, transition, link, search, project management
- Multi-instance support via
InstanceRegistryprotocol WorkTrackingServiceroutes operations to the correct adapter instance- Domain types (
WorkItem,CreateItemRequest,ItemCategory,SearchCriteria) are platform-agnostic ItemCategoryenum (TASK, SUBTASK, STORY, BUG, EPIC) -- callers express intent, adapters resolve to platform-specific types- Per-project type discovery and caching via createmeta API
Internal Module Pattern
Each messaging adapter follows the same decomposition:
src/appif/adapters/<platform>/
├── __init__.py # Public exports
├── connector.py # Connector protocol implementation
├── _auth.py # Authentication (protocol + implementation)
├── _normalizer.py # Platform message -> MessageEvent
├── _message_builder.py # MessageContent -> platform request (email adapters)
├── _poller.py # Inbound message detection (email adapters)
└── _rate_limiter.py # Retry + platform error -> domain error mapping
The Jira adapter uses a similar pattern with adapter.py (operations), _auth.py (YAML config + client), and _normalizer.py (API dicts to domain types).
Credential Setup
| Adapter | Auth Method | Setup Guide |
|---|---|---|
| Gmail | OAuth 2.0 (python scripts/gmail_consent.py <account>) |
docs/design/gmail/setup.md |
| Outlook | OAuth 2.0 (python scripts/outlook_consent.py <account>) |
docs/design/outlook/setup.md |
| Slack | Bot + App tokens from Slack app config | docs/design/slack/setup.md |
| Jira | API token in YAML config | docs/design/work_tracking/setup.md |
Documentation
| Document | Description |
|---|---|
| CHANGELOG.md | Version history, breaking changes, and migration guides |
| API Reference | Complete method signatures, domain models, and error types |
| ADAPTERS.md | Detailed adapter documentation (all platforms) |
| docs/usage.md | Unified messaging model and code examples |
| docs/design/gmail/ | Gmail design, technical design, setup |
| docs/design/outlook/ | Outlook design, technical design, setup |
| docs/design/slack/ | Slack design, setup, CLI checklist |
| docs/design/work_tracking/ | Jira requirements, design, technical design, setup |
| docs/adr/ | Architecture decision records |
License
GPL-3.0-or-later -- see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file appif-0.10.2.tar.gz.
File metadata
- Download URL: appif-0.10.2.tar.gz
- Upload date:
- Size: 236.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
819484149159d07c53afe61c4c0b0c846a6eb6ce7277d19663649700e07dd798
|
|
| MD5 |
416177f9088c11c8d2ba83c654c3c6be
|
|
| BLAKE2b-256 |
f00c6a010e27726250b9d3662106bef87b14efb10d8cde78162d64f709194309
|
File details
Details for the file appif-0.10.2-py3-none-any.whl.
File metadata
- Download URL: appif-0.10.2-py3-none-any.whl
- Upload date:
- Size: 85.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8356c71429cddafb483bdc0a56acc7f0db291415bf10c3aacf3b29d83218a6e0
|
|
| MD5 |
9cbb4e8271590535e2cb4c3f755723e4
|
|
| BLAKE2b-256 |
a2fa6ce31de8e6116c35ce3d6227c909e2bdb8b6fac81411547274dc4c4901b8
|