I started paying serious attention to Feishu MCP and Feishu CLI not because I wanted to write a technical-selection essay, but because they entered my daily workflow one after another.
At work, smartclaw first connected Feishu MCP, then later connected Feishu CLI. In my personal setup, I also wired both Feishu MCP and CLI into Claude Code and Codex. With MCP alone, the most visceral feeling was how quickly tokens disappeared: once tool definitions grew, a large chunk of context was gone before the agent had really started working. After CLI + skills entered the picture, the experience changed in a very concrete way — I even moved my main battlefield for Feishu-related work from Claude cowork to Claude Code.
That shift made me want to take the two layers apart properly. Over the same OpenAPI, why did Feishu end up with two agent connection forms this different? lark-mcp auto-generates 1,271 atomic tools; lark-cli + skills hand-curates a dozen high-frequency domains into commands and task-level shortcuts. After laying out both real codebases on my own machine — the tool descriptions, shortcut implementations, and their line-by-line mapping down to the underlying REST — the biggest thing I walked away with wasn’t “which one is more advanced.” It was why the same API naturally splits into two connection forms when the consumer is an agent.
Let me put the conclusion up front and unpack it layer by layer:
MCP and CLI are not two competing protocols. They are two points on one and the same “coverage × quality” frontier. What splits them apart isn’t whose interface design is cleverer — the caller on both sides is in fact the same reasoning model — it’s through what interface the model consumes the capability: structured tool-calling in context (MCP: the model picks a tool and fills params; the host executes and returns structured results), or acting like an operator at a terminal, firing a command string at a shell that can’t reason and reading back text (CLI). Different mode of consumption, different optimum.
And interestingly, public attention was nearly one-sided: a CLI born 11 months later pulled 20× the stars of Feishu’s MCP repo — and that MCP repo has gone quiet while the CLI still ships daily. This comparison is about Feishu’s two concrete wrappers, not the overall heat of the MCP protocol ecosystem. At least from public repo activity, Feishu’s own investment focus has clearly shifted toward CLI + skills.
This piece is that teardown, made legible: what Feishu’s MCP auto-generates and what it leaves unsolved; where the hand-curated CLI + skills layer is expensive and where it earns that cost; where the shared kernel lives; what that 20× gap does and does not prove in Feishu’s case; and what judgment framework falls out once you put the sample back into the real 2026 coordinates of MCP’s protocol boom and implementation-layer backlash.
1. The two packages, laid out on the table
Before any conclusion, let’s put both npm packages on the table and see what they actually are — every judgment later in this piece grows out of this one snapshot.
@larksuiteoapi/lark-mcp (the MCP face). Open the build output and the core is a gen-tools directory: auto-generated from Feishu’s OpenAPI spec, one REST endpoint to one tool. Count them — 1,271 tools across 52 domains (POST 540 / GET 435 / DELETE 124…), plus only 2 hand-written composite tools. But 1,271 is inventory, not exposure: at start-up the server registers a single preset.default ≈ 19 tools by default (IM 5 + Base 7 + Doc 6 + Contact 1); the rest open only on demand with -t.
@larksuite/cli (the CLI face). It is not a 1:1 mirror — it’s two layers. A thin schema layer: 120 methods across 9 services (calendar, drive, im, mail…), 1:1 with REST, schema-introspectable, each method linking straight to its official doc. A thick shortcut layer: about 126 +commands spanning ~11 domains (base 68 · task 12 · mail 11 · im 10…), often 1:N — one command orchestrating several endpoints, branching on parameters, or adding client-side logic (name→ID resolution, auto-pagination, query DSL).
The relationship between them is the real point. They look worlds apart, but underneath they stand on the same normalized descriptor IR: each MCP tool is one data descriptor (name / a description for the model / params schema / REST path / method); the CLI’s schema introspection output is also a structured IR (id / path / httpMethod / docUrl, even a per-param example). Capability is made into data — so one spec auto-renders into 1,271 MCP tools and into the CLI’s thin schema layer; the ~126 thick shortcuts, where CLI’s value actually lives, are hand-piled on top, not auto-generated. That distinction (auto-generated vs hand-curated) is the root of everything below.
| lark-mcp (MCP face) | lark-cli (CLI face) | |
|---|---|---|
| Relation to API | auto-generated · 1:1 | thin 1:1 + thick 1:N |
| Scale | 1,271 tools / 52 domains | 120 methods / 9 services + ~126 shortcuts |
| Default exposure | preset.default ≈ 19 |
command tree, by domain |
| Hand-built portion | just 2 composite tools | shortcut layer + Skill playbooks |
| Shared base | one descriptor IR: name/description/schema/path/method | same (CLI schema output is the IR) |
Three numbers to hold onto: MCP has 1,271 tools but exposes 19 by default; CLI has 120 thin commands + ~126 shortcuts; both faces share one IR. The next four steps work out, in turn: what MCP actually is; what CLI + skills actually is; where their shared kernel lives; and why users cast a 20× star vote for the CLI.
2. So what is MCP, actually
Those numbers overturn one common misconception about MCP, and expose one more important boundary.
The misconception: “it dumps a thousand-plus tools on the model” — it doesn’t. 1,271 is inventory; only a dozen-ish are exposed by default. Stuffing all 1,271 into context is precisely MCP’s most-criticized anti-pattern (I do that math in §6), and Feishu’s default config is already dodging it.
The real boundary: MCP’s expectation of the model is tool calling from the start. Open any tool and it’s a pure-data descriptor: a name, a description for the model, a params schema, plus the REST path and HTTP method. For a single tool call, what the model does really is choose the tool and fill arguments according to the schema. How the path is assembled, whether the method is GET or POST, where the token goes — a roughly 30-line generic handler does that off the descriptor. The model does not handle REST execution details.
But that also means the model really does have to assemble the tool-call chain. What it still does itself is chaining multi-step work. The tools are atomic (one tool = one endpoint); stringing together “look up the ID → call the write endpoint → retry on failure” is nobody’s job but the model’s. This is not a misconception; it is the boundary worth flagging — because who you hand “stringing atomic capabilities into a workflow correctly” to is one of the most central decisions in building a platform, and the real origin of every MCP-vs-CLI fork that follows.
Put it all together and MCP’s true shape is clear: it isn’t “a better API” — it’s a unified agent-service interface protocol. What it actually standardizes is “any agent discovering and invoking any service in the same way,” with runtime tool discovery at its core (connect to the server, the server announces what tools exist, the agent doesn’t have to read docs in advance).
But it carries a congenital weakness, which the teardown only confirmed: MCP standardizes transport, not semantics. Every vendor defines its own tool names and parameter structures, so ecosystem fragmentation merely shifts from “fragmented access methods” to “fragmented tool semantics.” That hole is the seed for everything later.
3. So what is CLI + skills, actually
If MCP’s keywords are protocol, discovery, and atomic tools, then CLI + skills is about curation, task wrapping, and operating procedure. It is not merely “MCP, but through a command line.” Over the same API, it adds two hand-built judgment layers: first wrap high-friction APIs into shortcuts, then write Skills that tell the agent how to use those shortcuts safely.
Use the MCP face as the contrast: it is strictly 1:1 with the API, 1,271 tools across 52 domains, marginal cost near zero. The model can reason, so it can consume “atomic capabilities” — even when it has to chain three steps itself. Everything Feishu does on this face is restraint in picking a default set + occasionally wrapping a high-frequency fixed flow into a composite tool (e.g. “create doc + pour in content” in one shot).
CLI + skills takes the other path: hand-curated, going for quality. Here’s a counterintuitive number: the CLI exposes only about 120 methods. But “120” isn’t “poor coverage” — it’s two levels of deliberate narrowing:
- Level one, domain selection (52 → 12): the CLI admits only the dozen-or-so end-user-facing domains, cutting the other 731 tools — all enterprise-back-office, HR, and PaaS long tail (corehr 218, hire 178, …). Real humans rarely touch these directly, let alone want them in a command line that’s supposed to be pleasant. (The three domain counts don’t conflict: schema’s 9 services are a subset of these dozen-ish curated domains — some domains have only shortcuts, no 1:1 commands.)
- Level two, a three-way split within each domain: every endpoint either enters the thin schema-command layer (atomic read/write, 1:1), or gets wrapped into a thick shortcut, or enters nowhere and is caught by the generic
apicommand.
The point is that thick-shortcut layer. Its unit of encapsulation is a “task,” not an “endpoint,” and its selection criterion is a single one:
Wherever the raw API has friction for an agent, wrap a layer. The criterion is “friction,” not “importance.”
I sorted the frictions Feishu’s shortcuts target into six kinds, each corresponding to something a human fills in from intuition and an agent stalls on:
| Friction | Evidence (Feishu shortcut) |
|---|---|
| ID resolution (agent has the name, not the ID) | +chat-search: look up chat_id by group name |
| Multi-endpoint orchestration / enrichment | +messages-search: internally calls mget and batch_query to enrich |
| Conditional branching | +record-upsert: POST or PATCH depending on whether record_id exists |
| Reliability | +messages-send: idempotency key built in |
| Security | +resources-download: forces a safe relative output path |
| Identity constraints | every shortcut explicitly marked bot-only / user-only |
Domains with high friction density get more shortcuts: Base 68 (almost every raw API has a pitfall), Task 12, Mail 11, IM 10. This layer isn’t “wrapping the API again” — it’s encoding the human engineer’s tacit know-how, explicitly.
The Skill layer needs to be called out separately, otherwise “CLI + skills” sounds like a vague slogan. A Skill is not a third API layer, and not a new execution environment. It is essentially an agent-facing playbook + routing rules + workflow constraints: when to use which domain, which reference to read first, which commands must not be called blindly, and when ambiguity requires asking the user. A Base skill, for example, says: in Base scenarios use lark-cli base +... shortcuts, do not fall back to raw api; read field structure before writing records; formula / lookup fields require their own guides; batch writes have a 200-record limit. A Calendar skill behaves more like a workflow gatekeeper: for scheduling or room search, read the schedule-meeting workflow first; without a concrete time, do not call +room-find; before creating an event, show candidate time / room options and wait for confirmation.
So the three layers are better read this way: the schema layer exposes atomic capabilities; the shortcut layer wraps high-friction APIs into tasks; the Skill layer tells the agent how to safely choose and compose those tasks in real context. What makes the CLI face feel usable is not merely that commands exist, but that these three layers together remove a lot of guessing from the agent.
The CLI also adds a batch of engineering capabilities MCP has none of, all because it works inside a shell + text pipe: output formats (--format json/csv/table), auto-pagination (--page-all), preview (--dry-run), identity switching (--as user/bot), and one gap that’s near-universal on the MCP side — dangerous-operation safety: the CLI marks 70 methods danger:true, and destructive commands require an explicit --yes.
By here the “mode of consumption determines form” line closes: the model does structured tool-calling in context and can tolerate atomic capabilities → MCP can just go for breadth; the model fires a command string at a shell that can’t reason, without MCP’s host-level structured tool-call schema constraint — commands can of course parse and validate arguments, but many choice errors, chaining errors, and business-semantics errors still surface only after the CLI runs → the CLI must use shortcuts and Skills to scrub as much friction as possible before delivery.
4. The real kernel isn’t either face — it’s the IR underneath
§1 already pointed out that the two faces share one descriptor IR. That deserves unpacking on its own — because that IR, not MCP or either face, is the system’s real asset. Laid out in full (Feishu’s source calls it McpTool), it’s one piece of pure data, not a piece of code:
interface McpTool {
name: string; // canonical dotted name: bitable.v1.appTableRecord.create
description: string; // the only natural-language field, fed to the LLM
schema: any; // params (zod), partitioned into data/path/params
path?: string; // REST template with :placeholders
httpMethod?: string;
accessTokens?: string[]; // ['tenant','user']
}
Two details make it the kernel: natural language is a single field, description; everything else is structured metadata. And path + httpMethod are mandatory — that’s exactly the backing for the “callable without depending on the SDK” point from §2. The roughly 30-line generic handler can dispatch all thousand-plus tools precisely because of this: split the dotted name, walk down the SDK object tree level by level; the moment the SDK hasn’t caught up with a new endpoint, fall back to firing raw HTTP straight from the descriptor’s path + httpMethod.
That “path fallback escape hatch” is the design I admire most: it decouples the generation layer (which must cover everything) from the SDK-wrapping layer (which is always half a step behind) — zero wait for new endpoints. But the bigger lesson is that one line: make “how to call it” data, not code. Precisely because capability is data, one IR auto-renders into MCP’s 1,271 tools and the CLI’s thin schema layer; change the spec, regenerate, humans never touch the output. But don’t overstate it: what auto-renders is only the “mechanical mirror” layer; CLI’s real value — those ~126 thick shortcuts — is hand-piled on top of the IR, precisely not part of the auto-generation (the very setup for §5’s “scalability is an illusion”).
For a platform building agent access, that’s the first transferable conclusion: the asset you should actually accumulate isn’t the MCP server, and isn’t the CLI — it’s that spec/IR shared by every face underneath. Only then can a quality improvement “be made once and propagate everywhere,” instead of being locked into the hand-maintenance of one particular face.
5. Then developer attention sent a signal — 20×
If the story stopped at §4, it would be a tidy “each has its strengths.” But interestingly, the public-attention numbers don’t line up with that tidy conclusion. Stars are not usage, and not retention, but they do tell you which entry point developers are willing to watch, save, and share. I put the two repos’ real data side by side (a snapshot from the teardown day, 2026-06-25):
| lark-openapi-mcp | lark-cli | |
|---|---|---|
| Stars | 742 | 14,552 (≈20×) |
| Created | 2025-04 | 2026-03 (11 months later) |
| Last commit | 2025-08 (~10 months quiet) | 2026-06 (still daily) |
Born 11 months later, 20× the stars — and Feishu’s MCP repo has gone dormant while the CLI is updated daily. From the public maintenance rhythm, Feishu’s investment focus at least clearly shifted toward CLI + skills. I stared at that table for a long time, and the conclusion converged to three points:
- Users feel “single-call quality”; they can’t feel “coverage breadth.” Nobody experiences the existence of those 731 long-tail APIs; everyone works inside their own dozen domains, and there the CLI is visibly better. Breadth is for the dashboard; quality is for the human.
- The layer users can actually feel as product value is the skills layer. What developers care about isn’t “raw capability” — it’s “task-level know-how,” the thing that actually lets the agent get the job done. The lesson for any platform opening capabilities to agents is direct: the real work is not only exposing endpoints — it’s this know-how layer.
- Scalable quality improvement is a layered illusion. What can “fix the generator once, everyone benefits” is only the description-quality layer (§6’s “writing tools = writing a prompt”); the friction removal and multi-step orchestration that actually move the experience aren’t in the generator at all — they can only be piled up by hand, one entry at a time. MCP holds the former and sounds more scalable, yet Feishu didn’t keep investing; the CLI chewed through that un-scalable hard part, and won the present.
(One distractor to clear first: the 20× isn’t won by “the CLI reaches more environments” — reachability is roughly even on both sides, and a shell-less environment can run only MCP. I unpack this in §7.)
Compress those three into one line, and it’s the thing I most want you to remember from this piece:
Auto-generating an API into a pile of MCP tools is table stakes — anyone can do it, and by itself it rarely creates user-perceived value. What actually decides life or death is the layer above it: friction removal + task encapsulation + skills. The 20× star gap at least points very clearly in that direction.
6. Put it back into 2026: MCP’s boom, and the backlash erupting alongside it
But I have to give that conclusion a timestamp and a boundary, or it gets misread as “MCP is useless.” It isn’t. §5 only says this: in Feishu’s sample, the auto-generated MCP wrapper did not beat the hand-curated CLI + skills layer. It does not prove that the MCP protocol ecosystem is failing. The real 2026 picture is far more complex than “CLI won.”
On one side is MCP’s victory at the protocol layer: more and more mainstream agent platforms are moving toward MCP compatibility. As “the unified AI-facing integration layer,” MCP is becoming the de facto default. In other words, “Feishu’s MCP repo has fewer stars” and “the MCP protocol is becoming more popular” are not contradictory: the former measures the product pull of one concrete provider wrapper; the latter measures adoption of a cross-platform integration protocol.
On the other side, the backlash also erupted in 2026 — and this is where the information edge lives:
- Context cost out of control. In Anthropic’s published example, definitions for 50+ MCP tools consumed about 72K tokens when loaded upfront. The 1,271 tools I saw in Feishu’s setup are the extreme form of this problem — which is exactly why it opens only 19 by default, no accident.
- It isn’t only Feishu’s users voting with their feet. In March 2026, Perplexity’s CTO said in a public developer discussion that the team was moving internally from MCP toward “traditional API + CLI tools,” citing token consumption, authentication friction, and reduced agent autonomy (summary of the public discussion).
More worth fully absorbing are the two answers Anthropic itself offered, because they point at where the frontier is moving:
- “Writing tools for an agent” is writing a prompt for the agent. Anthropic’s tool-design guidance explicitly treats tool-description work as prompt engineering, where small refinements can materially improve performance. That’s the same thing I saw in the CLI layer (“encode tacit know-how explicitly”), said two ways.
- Let the agent write code to call tools, instead of stuffing all tools into context. Anthropic’s Code execution with MCP exposes MCP tools as an explorable filesystem and has the agent read specific modules on demand — that’s progressive disclosure: don’t preload all capabilities; let the agent explore layer by layer and fetch context as needed.
This matters because it directly rewrites the boundary of §5’s conclusion: as models’ tool-retrieval and orchestration abilities strengthen, MCP’s “breadth + zero marginal cost” advantage may reclaim the long tail back from the CLI’s hand-curation. Feishu’s MCP actually already buries a recall meta-tool — “search in natural language for which API to call,” essentially RAG over tools — aimed squarely at that future. So “CLI won” is a conclusion about the present, not the endgame. That frontier line is still moving.
And MCP is making one more bet, this one unrelated to model capability and purely structural: it doesn’t depend on a shell / CLI environment. There’s already a small sample of this today — in a hosted / sandboxed agent environment with no shell (say Claude’s cowork sandbox), the CLI binary simply can’t exec, and only MCP can connect. But the real space is further out: phone apps, smart glasses, even AI devices that don’t exist yet — none of them can run a command-line binary, yet all of them can speak a protocol. As agents move off the developer’s terminal and onto millions of consumer devices, “protocol-native” turns from a niche perk into a moat the CLI structurally can’t follow. The CLI wins the present, where it lives inside a terminal; MCP is betting on the tomorrow where agents leave the terminal.
7. A Judgment Framework From The Feishu Sample
Having gone the full circle, the Feishu sample does not give a “side with MCP or side with CLI” answer. It gives a more transferable set of criteria.
First cut: by mode of consumption. This is the spine of the whole piece (note: the caller in both is a reasoning model — what differs is how and in what scenario it consumes):
- An uncertain set of services, discovered only at runtime (a general agent that doesn’t even know which services it’ll meet) → lean toward MCP’s atomic capabilities + runtime discovery.
- Deterministic scenarios, agent-in-loop, auditing or confirmation, or high-risk operations → lean toward the CLI’s curation + skills.
And let me puncture a myth I once believed myself: “the CLI reaches more environments” — it doesn’t. In local developer setups and common desktop-agent environments, reachability is roughly the same; an environment with no shell (hosted sandboxes, and tomorrow mobile / devices) can run only MCP (see §6). So you pick the CLI for the quality of its hand-curated layer, not because it “runs in more places.”
Second cut: by “first-party vs third-party.” This is one I’d settled earlier, orthogonal to the above: when your own official agent calls your own internal endpoints, the shortest path is Function Calling, direct — you don’t need MCP’s “service discovery + tool description + model selection” overhead at all. Only when you open capabilities to any third-party agent, plugging into the whole ecosystem, does MCP’s unified protocol start to pay off. In high-determinism scenarios with few, well-defined services, forcing MCP just adds latency and uncertainty.
Third cut: if you can only invest in one, which first. My lean: build CLI + skills first to capture present quality and adoption, with MCP following. But split MCP into two things here: the auto-wrapping layer is table stakes — anyone can do it, and it loses nothing by arriving later; the “protocol-native, reaches beyond the terminal” bet (§6), however, is a longer structural one — building CLI first is sequencing, not a claim that bet is unimportant. Doing both is, in essence, a hedge — MCP holds breadth + protocol-native reach, CLI + skills holds depth + experience.
Three more conclusions that fell out of this teardown and hold broadly for platform building:
- The base must share one spec/IR, so a quality improvement can “be made once and propagate everywhere” — don’t lock yourself into un-scalable hand-curation.
- The selection criterion for shortcuts / tools is “friction,” not “importance”: ID resolution, orchestration, idempotency, security, pagination, identity — hit a few of those six and it’s worth wrapping a layer.
- Dangerous operations must have marking + a confirmation mechanism (danger + explicit confirm) — a near-universal gap on the MCP side, and a place you can do better than Feishu.
And above all of this, don’t forget the larger judgment: the API is always the foundation, never the ceiling. Whether the layer above is a CLI, an MCP, or the agent reading docs itself, what gets called in the end is the same set of atomic APIs; the interface form is still converging, but the quality of the atomic API is an investment that never goes out of date.
8. Closing: the moat isn’t in the auto-wrapping layer
Tie the circle together: Feishu’s MCP and Feishu’s CLI are not mutually exclusive routes. They are two faces rendered from one IR, forked apart by who the consumer is; and the 20× star gap at least reminds us that the layer that auto-generates an API into tools — the layer anyone can do — is hard to turn into a moat on its own.
What’s actually valuable is the layer above: encoding the human engineer’s tacit know-how, entry by entry, into task wrappers where “the friction has been scrubbed clean.” And the irony of that layer is — it’s precisely the part that doesn’t auto-scale. Auto-generation has zero marginal cost and no takers; hand-curation costs a fortune and won the present. This runs into the same wall as the ending of my last piece: what finally blocks a thing you can technically build is, more often than not, not the technology.
Further out there may be an endgame: when API docs are sufficiently structured and machine-readable, a doc and an MCP tool definition become the same object — metadata is the doc, the doc is the interface — and “API integration” as a concept fades out. But we’re not there. At today’s moment, the bottleneck is still mundane: who is willing to spend the human labor to smooth that friction layer flat, one entry at a time.
That is the real value of the Feishu sample: it puts the most expensive, least automated, and easiest-to-underestimate work between “protocol access” and “task usability” directly on the table.