mcpField
C
creating-letta-code-channels

creating-letta-code-channels

github.com/letta-aiDevelopment152107 installsUpdated 4w ago
Repository

About creating-letta-code-channels

A skill for developers adding or debugging channels in Letta Code. It explains when to use dynamic user plugins versus first-party channels and how to implement, route, and test replies.

Key features

  1. 01Choosing user plugins or first-party channels
  2. 02Scaffolding a user channel plugin
  3. 03Implementing tool and relay reply modes
  4. 04Testing discovery, routing, and delivery
  5. 05Handling pairing and public-channel safety

Use cases

  1. 01Prototyping a headless channel plugin
  2. 02Adding a channel with custom Desktop UI
  3. 03Debugging inbound routing and pairing
  4. 04Verifying outbound reply behavior

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/letta-ai/skills --skill creating-letta-code-channels

After installing, type/creating-letta-code-channelsin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

references/
first-party-channels.md
testing.md
user-plugins.md
scripts/
scaffold-user-channel-plugin.ts

Details

From the project README

Creating Letta Code channels

Use this when adding or debugging Letta Code channel support.

First choice

  • User plugin (~/.letta/channels/<id>/) for headless experiments, community plugins, and fast workflow tests.
  • First-party channel (src/channels/<id>/) when the channel needs bespoke Desktop UI, custom account snapshots, Slack/Discord-style auto-routing, rich protocol fields, or migration/compatibility shims.

User plugins cannot shadow first-party ids: telegram, slack, and discord are ignored under ~/.letta/channels/. Use ids like telegram-test, whatsapp-community, or custom-chat.

Core workflow

  1. Work in ~/letta/letta-code or a worktree.
  2. Read src/channels/README.md on branches with dynamic plugins.
  3. For a user plugin, create:
    • ~/.letta/channels/<id>/channel.json
    • ~/.letta/channels/<id>/plugin.mjs
    • ~/.letta/channels/<id>/accounts.json
  4. Implement messageActions for both reply modes: explicit tool replies and automatic relay text use the common outbound adapter path.
  5. Start with dmPolicy: "pairing" for testing, or allowlist/open for known headless deployments.
  6. Test all four legs:
    • plugin discovery/import
    • inbound adapter.onMessage(msg) to route/pairing
    • routed channel notification reaches the agent
    • outbound behavior matches the account's captured reply mode: explicit MessageChannel delivery in tool mode, or automatic finalized-text delivery in relay mode
  7. Run targeted tests, then bun run typecheck, bun run lint, bun run build.

References

Read only what is needed:

  • references/user-plugins.md — dynamic plugin manifest/account/runtime/headless flow and gotchas.
  • references/first-party-channels.md — first-party channel file cascade and safety checks.
  • references/testing.md — smoke-test checklist and commands.

Scaffold helper

Use the bundled scaffold for a minimal user plugin skeleton. Replace <path-to-this-skill> with this skill directory path:

npx tsx <path-to-this-skill>/scripts/scaffold-user-channel-plugin.ts \
  my-channel "My Channel" \
  --runtime-package [email protected] \
  --runtime-module some-sdk

It creates channel.json, plugin.mjs, and accounts.example.json. Replace the TODO inbound/outbound implementation with the real SDK calls.

Hard lessons

  • Accounts default to tool mode when reply_mode is unset or invalid. Add "reply_mode": "relay" to an existing saved account, preserving its actual ID, root fields, and credentials.
  • Relay requires a build containing PR 4043; v0.34.1 predates it. The on-device gateway implements delivery; remote hosts must supply transport and captured per-input policy. This is a host distinction, not an agent-state restriction. Mode changes affect new inputs, not active or queued ones.
  • A single-destination relay input sends finalized text without MessageChannel; unfinished error/cancel text, reasoning, and subagent output are excluded. Multi-destination inputs require explicit replies, never broadcasts. Queued inputs stay separate. Use tool mode for reactions, files, and proactive sends.
  • For public channels, suppress tool approval/control prompts unless there is verified operator routing. Posting approval prompts publicly leaks tool input and invites forged approve replies.
  • User plugin runtime resolution must not count parent/dev node_modules. Runtime modules should resolve from explicit runtime dirs only.
  • Headless pairing is CLI-first: letta channels pair --channel <id> --code <code> --agent <agent-id> --conversation <conversation-id>.
  • Running listeners reload pairing.yaml and routing.yaml on the next inbound miss; restart only when adapter/account config itself changed.

Comments

Markdown supported