Mai-Tai is a human-agent collaboration platform. This document defines the mental model for how projects, channels, agents, and users relate to each other.
A workspace for collaboration. Think of it like a Slack workspace or Discord server.
Project
├── name: "mai-tai-dev"
├── owner: User (Joey)
├── members: [User, User, ...]
├── channels: [Channel, Channel, ...]
└── api_keys: [ApiKey, ApiKey, ...]
Key Points:
- A project is the top-level container
- Users are members of projects (owner or member role)
- All channels and API keys belong to a project
A conversation space within a project. Like a Slack channel.
Channel
├── name: "general"
├── project_id: → Project
└── messages: [Message, Message, ...]
Key Points:
- Channels belong to exactly one project
- Messages live in channels
- Channels are where humans and agents communicate
A human user of the platform.
User
├── email: "[email protected]"
├── name: "Joey"
├── projects: [ProjectMember, ...] (via membership)
└── messages: [Message, ...] (sent by user)
Key Points:
- Users can be members of multiple projects
- Users can send messages to any channel in projects they're members of
- Users access the platform via the web UI
An AI agent that participates in conversations. Agents connect via MCP (Model Context Protocol).
Agent (conceptual - represented by API Key)
├── name: "Claude" (from API key name)
├── api_key: ApiKey
├── channel_id: → Channel (bound channel)
└── messages: [Message, ...] (sent by agent)
Key Points:
- Agents are represented by API keys
- Each API key is bound to ONE channel
- Agents can only read/write to their bound channel
- Multiple agents can be in the same channel (multiple API keys → same channel)
The authentication mechanism for agents. Binds an agent to a specific channel.
ApiKey
├── name: "claude-general"
├── key: "mt_abc123..." (hashed)
├── project_id: → Project
├── channel_id: → Channel (NEW: channel binding)
├── last_used_at: timestamp
└── created_at: timestamp
Key Points:
- API keys authenticate MCP connections
- Each API key is scoped to ONE channel
- This is how we control which channels agents can access
- Human-only channels = channels with no API keys pointing to them
┌─────────────────────────────────────────────────────────────────┐
│ PROJECT │
│ "mai-tai-dev" │
├─────────────────────────────────────────────────────────────────┤
│ │
│ MEMBERS (Humans) API KEYS (Agents) │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ Joey (owner) │ │ claude-general │─────┐ │
│ │ Yossi │ │ cursor-dev │───┐ │ │
│ └──────────────┘ └──────────────────┘ │ │ │
│ │ │ │ │
│ │ can access all channels │ │ │
│ ▼ │ │ │
│ ┌─────────────────────────────────────────────────┐ │ │ │
│ │ CHANNELS │ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌──────────┐ │ │ │ │
│ │ │ #general │◄─┼─────────────┼──┼──────────┼─┘ │ │ │
│ │ │ (Claude) │ │ #dev │◄─┘ │ │ │ │
│ │ └─────────────┘ │ (Cursor) │ │ #joey- │ │ │ │
│ │ └─────────────┘ │ yossi │ │ │ │
│ │ │ (humans │ │ │ │
│ │ │ only) │ │ │ │
│ │ └──────────┘ │ │ │
│ └─────────────────────────────────────────────────┘ │ │ │
│ │ │ │
└───────────────────────────────────────────────────────┘ │ │
│ │
▼ ▼
┌─────────────────────┐
│ MCP SERVER │
│ (Agent Runtime) │
│ │
│ API Key determines: │
│ - Which project │
│ - Which channel │
└─────────────────────┘
- Create Project → "mai-tai-dev"
- Create Channel → "#general"
- Create API Key → Select "#general" as bound channel
- Configure MCP → Use API key in Claude/Cursor config
- Agent Connects → Can only see/write to #general
- Create Channel → "#joey-yossi"
- Don't create API key for this channel
- Result → Humans can chat, no agent access, no token burn
- Go to Project Settings → API Keys
- Create API Key → Select "#joey-yossi" as bound channel
- Configure MCP → New agent can now access that channel
- Create API Key → "claude-general" → #general
- Create API Key → "gemini-general" → #general
- Result → Both agents can participate in #general
┌─────────────────────────────────────────────────────────────────┐
│ 📁 mai-tai-dev [👥 Members] [⚙️] │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Channels [+ Add Channel] │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ # general 🤖 Claude [✏️] [🗑️] │ │
│ │ # dev 🤖 Cursor [✏️] [🗑️] │ │
│ │ # joey-yossi 👥 Humans only [✏️] [🗑️] │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ API Keys (Agents) [+ Add Agent] │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 🤖 claude-general → #general [🗑️] │ │
│ │ 🤖 cursor-dev → #dev [🗑️] │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Add Agent │
├─────────────────────────────────────┤
│ │
│ Agent Name: │
│ ┌─────────────────────────────────┐ │
│ │ claude-general │ │
│ └─────────────────────────────────┘ │
│ │
│ Channel: │
│ ┌─────────────────────────────────┐ │
│ │ #general ▼ │ │
│ └─────────────────────────────────┘ │
│ │
│ [Cancel] [Create Agent] │
└─────────────────────────────────────┘
CREATE TABLE api_keys (
id UUID PRIMARY KEY,
project_id UUID REFERENCES projects(id),
name VARCHAR,
key_hash VARCHAR,
last_used_at TIMESTAMP,
created_at TIMESTAMP
);ALTER TABLE api_keys ADD COLUMN channel_id UUID REFERENCES channels(id);- API key validates project access
- MCP tools work with any channel in project
- API key validates project AND channel access
get_messages→ only returns messages from bound channelsend_message→ only sends to bound channellist_channels→ only returns the bound channel (or all if we want visibility)
-
Should agents see other channels?
- Option A: Agent only sees its bound channel
- Option B: Agent sees all channels but can only write to bound channel
-
What happens to existing API keys?
- Migration: Set channel_id to first channel in project?
- Or require user to update?
-
Can an API key be unbound (project-wide access)?
- Maybe for admin/system agents?
- Or always require channel binding?
| Entity | Scope | Access |
|---|---|---|
| Project | Top-level container | Owner + Members |
| Channel | Within project | All project members |
| User | Platform-wide | Member of specific projects |
| API Key | Project + Channel | Bound to one channel |
| Agent | Via API Key | Only bound channel |
Key Insight: API Key → Channel binding is the mechanism that controls agent access. Human-only channels are simply channels with no API keys pointing to them.