dsh-feishu
Manifest validDeep Feishu / Lark integration for DeepSeek Harness: create Bitable apps, docs, sheets, wiki nodes, uploads and messages in the user's own cloud workspace, plus a user OAuth flow.
dsh-feishu
English | 中文
Deep Feishu / Lark integration for DeepSeek Harness.
It gives the agent eight tools that act on a real Feishu tenant (and a browser panel that shows what it did and fixes authorization in one click):
| Tool | What it does |
|---|---|
feishu_bitable | Create Bitable (多维表格) apps with their tables and fields in one call; list tables and fields; insert, search, update, and delete records in batches |
feishu_dashboard | Create dashboards and chart blocks inside a Bitable (柱状图 / 折线图 / 饼图 / 排行榜 / 指标卡 / NPS … 15 types), position them, and read a chart's computed numbers |
feishu_query | Run a grouped, aggregated query over a table (group by, sum / avg / min / max / count / distinct_count, filter, sort, top-N) with the aggregation done server-side |
feishu_doc | Create documents; append Markdown (converted to native blocks), append raw blocks, read plain text, or import a local Markdown/Word/Excel/PowerPoint/PDF file as a new cloud document |
feishu_sheet | Create spreadsheets; add sheets; read, write, and append ranges |
feishu_drive | Browse folders, create folders, upload and download files, move, delete, read metadata |
feishu_wiki | List wiki spaces and nodes, resolve a wiki token to its document, create and move nodes |
feishu_message | Send text, interactive cards, rich posts, files, and images; reply in a thread; post through a custom-bot webhook |
feishu_auth | Diagnose authorization, produce the authorization URL, complete a manual code exchange, refresh, and log out |
feishu_api | Call any documented Feishu open-platform path, with the same auth, retry, and error mapping |
New content lands in the user's own cloud drive because the tools act as the authorized user, not as the app. When no user grant is stored they fall back to the app identity and say so in a warning rather than silently filing documents into a space the user cannot see.
Quick start
1. Create a Feishu app
-
Open the Feishu Open Platform (or Lark) and create a custom app.
-
Under 凭证与基础信息, note the App ID and App Secret.
-
Under 安全设置 → 重定向 URL, add the callback this plugin serves:
http://127.0.0.1:19387/api/feishu/oauth/callbackThe port is whatever DeepSeek Harness serves on (the plugin reads it from the host and reports the exact URI in
feishu_authactionstatus). -
Under 权限管理, add the scopes below, then publish a version — scopes only apply after the version is released:
Scope Needed for offline_accessRequired — without it Feishu returns no refresh token and the user must re-authorize every couple of hours bitable:appBitable docx:documentDocuments drive:driveDrive, imports, uploads sheets:spreadsheetSpreadsheets wiki:wikiWiki im:message,im:resourceMessages and file/image sending contact:user.base:readonlyReading the authorized user's name
2. Enable the plugin
See Install below, then restart DeepSeek Harness.
3. Configure it in the GUI
Open 设置 → 飞书 / Feishu. Everything is editable there — no YAML by hand:
- 应用凭据 — the App ID, the App Secret (write-only), the credential reference name, and the brand (飞书 / Lark).
- 默认落点与身份 — the folder new content lands in, and which identity the tools use by default.
- 功能开关 — whether the tools register, whether the HTTP routes mount, whether the floating panel shows, and the tool-name prefix.
- 授权 — the scopes to request and the redirect URL to register.
- 高级 — endpoint overrides and the page-size caps.
Saving writes only this plugin's entry in the profile's cordis.patch.yml,
which the harness hot-reloads, so most changes apply without a restart. The App
Secret is never written to that file: it goes straight into the credential
store under the reference name, and the page only ever shows whether it is set.
4. Authorize
Press 授权飞书 on the settings page, or ask the agent to run feishu_auth
with action authorize_url. The browser lands on the callback, the grant is
stored, and every later call acts as that user. The same form shows the redirect
URL to register on the Feishu app and lists the scopes to grant.
Host without a reachable browser callback (a remote or headless session)? Open the URL anywhere, then pass the
codequery parameter tofeishu_authwith actioncomplete.
Install
Two ways, and they are alternatives — do not do both. Each creates a loader
entry with id feishu, and the loader rejects a composition that defines one id
twice with duplicate loader entry id.
As a bundle (recommended)
The package declares a dsh.bundle patch, so a bundle-aware CLI installs and
mounts it in one step:
dsh plugin add github:2048lr/dsh-feishu
That appends dsh-feishu to dsh.profile.bundles and profile boot merges
cordis.patch.yml, which inserts the plugin row. Nothing else
needs editing.
The settings page is aware of this: when the profile mounts the bundle, it writes
an id-targeted config override rather than a second insert, so saving from
the GUI never breaks the composition.
By hand (no CLI)
The plugin must be resolvable from the profile's node_modules, so it is copied
there:
# from the repository root
pwsh -File scripts/install.ps1
The script copies the package into the profile's node_modules and inserts the
entry into cordis.patch.yml:
- insert:
- id: feishu
name: dsh-feishu
config:
appId: cli_xxxxxxxxxxxx
# appSecretRef: FEISHU_APP_SECRET
# brand: feishu # or lark
# defaultFolderToken: fldcnxxxxxxxx
Restart DeepSeek Harness. The host half is a Node module cached in-process, so a reload alone does not pick it up. The browser half reloads with the page.
After that first restart, use 设置 → 飞书 / Feishu instead of editing the YAML: the page rewrites the block the installer added and the harness hot-reloads it.
Why the package avoids @deepseek-ai/* imports
A third-party plugin is loaded from a junction or a copied directory inside the
profile, where a bare @deepseek-ai/dsh-tools specifier resolves to whichever
DSH installation is reachable — which may not be the running host. The host half
therefore imports only Node builtins and hand-writes its tool definitions
and its configuration validation. The test suite checks every generated schema
against the real SDK validator, so the hand-written form cannot drift from what
the registry accepts.
Configuration
All fields are optional except appId.
| Field | Default | Meaning |
|---|---|---|
appId | — | The Feishu app id. Required. |
appSecretRef | FEISHU_APP_SECRET | Credential reference resolved through the harness credential store, then the environment. |
appSecret | '' | Literal fallback. Discouraged: it puts a secret in configuration. |
brand | feishu | feishu (open.feishu.cn) or lark (open.larksuite.com). |
apiBaseUrl / accountsBaseUrl | per brand | Override the endpoints (for a proxy or a private gateway). |
defaultFolderToken | '' | Where new documents, sheets, Bitable apps, and uploads go when a call omits a folder. |
defaultIdentity | auto | auto, user, or app. auto uses the user when authorized and otherwise the app, reporting the fallback. |
toolPrefix | feishu_ | Rename the tools (for example fs_). |
requestTimeoutMs | 30000 | Per-attempt timeout. |
maxRetries | 2 | Total attempts for rate-limited or transient failures (capped at 6). |
enableTools | true | Register the model-facing tools. |
enableOAuth | true | Mount the /api/feishu/* routes (status, activity, config, OAuth). |
enableUi | true | Show the floating panel. |
oauthRedirectUri | auto | Override the callback. Empty means "use the host webserver's loopback address". |
oauthScopes | recommended set | Scopes requested by the authorization flow. |
registerSystemPrompt | true | Append the Feishu guidance section to the system prompt. |
activityLogSize | 50 | Entries retained for the browser panel. |
maxRecordsPerPage | 500 | Page-size cap for paginated Feishu reads. |
patchFilePath | auto | The profile patch file the settings page edits. Not writable from the browser. |
userAccessToken | '' | Static user token for headless use; skips OAuth entirely. |
Environment fallbacks: FEISHU_APP_ID, FEISHU_APP_SECRET, FEISHU_BRAND,
FEISHU_FOLDER_TOKEN, FEISHU_USER_ACCESS_TOKEN.
The two Bitable API generations
The plugin uses both, deliberately:
- V1 (
/bitable/v1/apps/...) for apps, tables, fields, and records. It is the documented public API, and it is what the official Lark/Feishu SDK ships. - V3 (
/base/v3/bases/...) for the two capabilities V1 does not have at all: dashboards with charts, and server-side aggregation. Note the resource isbases, notapps— the V1 and V3 paths are not interchangeable, and asking the wrong one answers a plain-text404 page not found, not a business error.
V3 is newer than the published SDK, so it is not discoverable from the SDK's typings; the shapes here were taken from the official Lark CLI and verified against the live API.
| V1 | V3 | |
|---|---|---|
| Field types | numbers (1 = text) | strings ("text") |
| Records | row objects {record_id, fields} | columnar data matrix + field_id_list |
| Batch writes | {records: [{fields}]} | record_id_list / by_filter + patch |
| Aggregation | none | POST .../data/query, a DSL naming tables and fields |
| Charts | none | dashboards + dashboards/{id}/blocks |
A chart block is described by one data_config object, which names a table and
its fields rather than addressing them by id:
{
"table_name": "订单表",
"series": [{ "field_name": "金额", "rollup": "SUM" }],
"group_by": [{ "field_name": "类别", "mode": "integrated", "sort": { "type": "value", "order": "desc" } }],
"filter": { "conjunction": "and", "conditions": [{ "field_name": "状态", "operator": "is", "value": "已完成" }] }
}
Use count_all: true instead of series to count rows, and {"text": "…"}
for a text block. Aggregations live in feishu_query rather than here, so a
question like "which region sold most" is one call instead of a table scan.
How it works
lib/
index.js composition root: config, identity, client, tools, routes
config.js normalization, brand endpoints, defaults
identity.js app credentials, tenant/app tokens, user grant, OAuth, refresh
http.js one HTTP request: timeout, bounded retries, byte ceiling
client.js the Feishu wire contract: envelope, auth, multipart, pagination
errors.js failure shaping and per-code remediation hints
local-file.js path resolution against the session workspace, upload/download
activity.js bounded in-memory record for the panel
entry-config.js safe reader/writer for this plugin's own loader entry
routes.js /api/feishu/{status,activity,config,oauth/*}
tools/
kit.js JSON-Schema-subset validator, definition helper, result envelope
auth.js bitable.js docx.js sheet.js drive.js wiki.js im.js raw.js
index.js assembly, prefixing, activity tracing
ui/client.js browser half: the floating panel and the settings page
The settings page
The page is self-contained: it talks to this plugin's own
/api/feishu/config endpoint rather than the harness settings service, so the
plugin keeps importing nothing from the SDK. A save does three things:
- validates every field against the table in
routes.js— the redirect URL and the API base URLs must behttp(s)://, the brand and identity are enumerations, the numeric fields are range-checked, andpatchFilePathis not writable from the browser at all; - rewrites only this plugin's entry in the profile patch file, which the
harness hot-reloads.
entry-config.jsaccepts only a path shaped like.../profiles/<name>/cordis.patch.yml, locates the block by its markers (or by a structural scan for the entry that names the plugin), carries over any key the page does not manage verbatim, writes atomically, and keeps one backup; - stores the App Secret through
ctx.credentials.set, so the secret never reaches the file and is never returned by any endpoint.
A field submitted as null is removed from the entry, which is how the page
resets a value to the plugin default.
Token custody
- App secret — resolved through the credential store, then the environment, then literal configuration. It never leaves the host and is never returned by the status endpoints.
- Tenant / app access tokens — minted on demand, cached in memory, re-minted two minutes before expiry, single-flight so concurrent calls do not race.
- User access token — a durable grant, so it lives in the credential store
as the record
dsh-feishu/user-token, together with its refresh token, and survives restarts. It is refreshed lazily when it nears expiry, and once more if Feishu rejects it mid-call.
Failure handling
Feishu reports business failures as HTTP 200 with a non-zero code. The client
unwraps both shapes into a FeishuError carrying the code, the HTTP status, the
path, any permission_violations, and a remediation hint. A token-expiry code
earns exactly one automatic refresh-and-retry; everything else is surfaced with
the hint attached, because "the app lacks a scope" and "the user never
authorized" need different fixes.
Identity
Every tool takes an identity argument (auto, user, app). user puts
new documents in the authorized person's drive and is the default; app acts as
the application. auto prefers the user and, when none is stored, uses the app
and reports the fallback as a warning, so a document never quietly ends up in
the wrong drive.
Development
npm test # or: node test/run-tests.mjs
The suite runs without the harness. It drives a mock Feishu tenant through a
fake fetch, and it validates two things against the real DSH SDK rather
than a local reimplementation:
- every tool parameter and output schema passes the SDK's
assertSupportedJsonSchema; - every parameter schema renders through
jsonSchemaToTs, so PTC mode keeps working.
The SDK is deliberately not a dependency, so the suite looks for the real
@deepseek-ai/dsh-tools in the usual profile and global locations and skips
those two checks when it cannot find it (which is what happens in CI). Point
DSH_TOOLS_PATH at dsh-tools/lib/index.js to force a specific copy.
Security notes
- The app secret is read by reference and never returned over HTTP; the status endpoint reports only whether it is set and where it came from.
- The OAuth routes are loopback-only and same-origin. The callback validates the
stateit issued and refuses a mismatch. - Bearer tokens are never logged, and the activity ring stores summaries and URLs, not credentials.
feishu_apiis a full escape hatch by design. Register it only in compositions where the agent is trusted with the app's scopes.
Uninstall
pwsh -File scripts/uninstall.ps1
Then restart DeepSeek Harness and delete the stored grant if you want the user authorization gone as well.
Related projects
Feishu / Lark integrations are a crowded space, so it is worth being precise about what this plugin is and is not. Everything below is a point-in-time description; follow the links for the current state.
Official
- larksuite/lark-openapi-mcp
(MIT, npm
@larksuiteoapi/lark-mcp) — the official Model Context Protocol server, and by far the broadest option: it wraps the published OpenAPI as MCP tools with presets, spanning IM, calendar, contacts, approval, drive, and more. Two caveats matter for document work, and both come from its own README and tool list: editing Feishu cloud-document content is stated as unsupported (import and read only), and its Bitable coverage is the V1 surface, where a dashboard appears only aslistandcopy— no creating or updating a dashboard or a chart block, and no aggregation. - larksuite/cli — the official CLI, with
lark-baseskills and reference documents. This plugin's V3 endpoint shapes and itsdata_configschema were taken from here and then verified against the live API.
Community Feishu MCP servers
- shou-nian/feishu-mcp-server
- leemysw/feishu-docx — Docs, Sheets, and Bitable in both directions with Markdown, built for agents
Other DeepSeek Harness plugins
The DSH plugin market lists well over a hundred Feishu/Lark plugins, but the overwhelming majority are IM bridges — talk to your agent from Feishu, approval cards, notifications — which is a different job from this one. The closest in kind are:
- zhengjy01/dsh-feishu-mcp — Feishu OpenAPI exposed to DSH over MCP
- Mr-SYGao/dsh-feishu-reader — reads Feishu docs, wiki, sheets, and Bitable (read-only)
- meliwanx/dsh-office-cli — wraps the official WeCom / DingTalk / Feishu CLIs
- shrekcg/dsh-im-channel — IM access that also bundles a set of Feishu MCP tools
What is different here
This plugin is deliberately narrow and deep. Where it goes beyond the alternatives above:
- Writing, not just reading — Markdown converted into native document blocks, local files imported server-side, spreadsheet writes, Bitable records.
- Charts and dashboards through the V3 API, including reading back a chart's computed numbers.
- Server-side aggregation (
feishu_query), so "top N by …" is one call instead of a table scan. - User-identity OAuth with an auto-refreshing grant, so new content lands in the user's own drive rather than the app's space.
- A visual settings page, and errors carrying a per-code remediation hint.
For anything outside that list — calendar, approval, contacts, IM — reach for the
official MCP or the official CLI, or use the feishu_api escape hatch.
License
MIT © 2026 Run Liu
Feishu, Lark, and 飞书 are trademarks of their respective owners. This project is an independent integration and is not affiliated with or endorsed by them.
Comments
Loading…
From the same category
by ccch1mneyyy
DSH 官方公众号收录的 TUI 补位插件:Claude Code 风,鲸鱼顶栏/实时状态/流式思考/双击 Esc 回滚/上下文进度+TPS。npm 一键装。 DSH official WeChat featured TUI plugin — Claude Code style: whale bar, live status, streaming thoughts, double-Esc rol
★ 4.6k
↓ 11.9k/wk
MIT
TypeScript
Oct 16, 2026
dsh plugin --profile terminal add @deepseek-harness-tui/dsh-tuiby strukto-ai
The World's First Virtual Terminal for AI Agents
★ 3.7k
↓ 374/wk
Apache-2.0
TypeScript
Oct 16, 2026
dsh plugin --profile agent add @struktoai/mirage-dshby bowenliang123
The best DeepSeek Harness plugin for context insight and management, with context dashboard / browser / sidebar and context command, for context statistics, composition, breakdown, evolution details,
★ 2.2k
↓ 52.1k/wk
Apache-2.0
TypeScript
Oct 16, 2026
dsh plugin --profile web add dsh-contextby huiliyi37
官方 DeepSeek Harness 的交互式终端 UI 插件:自研 ANSI 极简交互渲染、流式 Markdown/工具卡、16+ 主题、slash 命令与选择器、输入历史与本地偏好持久化、LSP 诊断、memory记忆,很丝滑的开发体验。
★ 286
↓ 486/wk
Apache-2.0
TypeScript
Sep 30, 2026
dsh plugin --profile terminal add @huiliyi37/dsh-tianshu-tuiby Hilbert-beinghappy
面向 DeepSeek Harness 的 Claude Code 风格终端界面,支持 Windows、macOS 与 Linux,兼容透明终端、VS Code 主题和自定义配色。
★ 196
↓ 108/wk
MIT
TypeScript
Oct 13, 2026
dsh plugin --profile web add seekttyby c3cvld7aag
富江DSH完美破甲|富江破甲 DSH 本地插件,内置提示词规则,支持 Windows 一键安装、环境自动准备与一键卸载,全中文使用说明。
★ 94
JavaScript
Oct 3, 2026
dsh plugin --profile web add fujiang-armor