---
sidebar_position: 3
title: Lark
---

# Lark skill

Send messages, reply in threads, list chats, and read chat history on Lark / Feishu.

- **ID:** `lark`
- **MCP server:** `lark` (tools exposed as `mcp__lark__*`)

## Tools provided

| Tool | What it does |
|---|---|
| `lark_send_message` | Send a text message. `receive_id` accepts chat_id (`oc_*`), open_id (`ou_*`), union_id (`on_*`), or email — the receive id type is inferred from the prefix |
| `lark_reply` | Reply to a specific `message_id` (creates a thread) |
| `lark_list_chats` | List chats the bot is a member of |
| `lark_get_chat_history` | Fetch recent messages in a `chat_id` (newest first), default `page_size: 20` |

## Setup

Lark bots authenticate with App ID + App Secret. The bot needs `im:message` and `im:chat:readonly` (or wider) permissions, and must be added to any chat it should read or post into.

1. Create a custom app at [open.feishu.cn](https://open.feishu.cn/) (or Lark's open platform).
2. Enable **Bot** capability and grant the IM permissions above.
3. In the Zibby dashboard, **Settings → Integrations → Connect Lark**, paste your `App ID` and `App Secret`.
4. The backend exchanges them for a `tenant_access_token` on demand and caches it (TTL ~100 min, just under Lark's 2h).

For self-hosted Lark deployments, set `host` on the integration config; the default is the standard Feishu host.

## Use in an agent

```js
import { WorkflowAgent, WorkflowGraph } from '@zibby/core';
import { SKILLS } from '@zibby/skills';

export class IncidentNotifier extends WorkflowAgent {
  buildGraph() {
    const graph = new WorkflowGraph();
    graph.addNode('notify', {
      agent: 'claude',
      skills: [SKILLS.LARK],
      prompt: (state) => `Post a summary of incident ${state.incidentId} to the on-call
      chat (chat_id: oc_xxxxx). If a previous thread exists in state.threadMessageId,
      use lark_reply instead of lark_send_message.`,
    });
    return graph;
  }
}
```

## Output example

`lark_send_message`:

```json
{ "ok": true, "message_id": "om_d2e6e3a1f24c9d3..." }
```

`lark_get_chat_history`:

```json
{
  "messages": [
    {
      "message_id": "om_abc...",
      "sender_id": "ou_xyz...",
      "sender_type": "user",
      "msg_type": "text",
      "content": "{\"text\":\"deploy is green\"}",
      "create_time": "1715839203000"
    }
  ]
}
```

## Implementation notes

Spawns `packages/skills/bin/mcp-lark.mjs`. Like Sentry, auth is fetched through the backend's `resolveIntegrationToken('lark')` endpoint and the tenant access token is cached in-process.

`receive_id_type` is required by Lark and is inferred from the id prefix (`oc_` → chat_id, `ou_` → open_id, `on_` → union_id, `cli_` → app_id, anything with `@` → email). Callers pass whichever id they have.

The MCP server uses `alwaysLoad: true` so tools land in the system prompt — same fix as Sentry. The `assistant` agent strategy can call the in-process `handleToolCall` for OpenAI Assistant API runs.
