<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: samz</title>
    <description>The latest articles on DEV Community by samz (@amzani).</description>
    <link>https://dev.to/amzani</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1360929%2F7ab30f29-a2e7-47fd-ac7a-35df76f0a82a.jpeg</url>
      <title>DEV Community: samz</title>
      <link>https://dev.to/amzani</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/amzani"/>
    <language>en</language>
    <item>
      <title>Your MCP Server Is Eating Your Context Window. There's a Simpler Way</title>
      <dc:creator>samz</dc:creator>
      <pubDate>Mon, 16 Mar 2026 15:31:45 +0000</pubDate>
      <link>https://dev.to/apideck/your-mcp-server-is-eating-your-context-window-theres-a-simpler-way-315b</link>
      <guid>https://dev.to/apideck/your-mcp-server-is-eating-your-context-window-theres-a-simpler-way-315b</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; MCP tool definitions can burn 55,000+ tokens before an agent processes a single user message. We built the Apideck CLI as an AI-agent interface instead:an ~80-token agent prompt replaces tens of thousands of tokens of schema, with progressive disclosure via &lt;code&gt;--help&lt;/code&gt; and structural safety baked into the binary. Any agent that can run shell commands can use it. No protocol support required.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem demos never show you
&lt;/h2&gt;

&lt;p&gt;Here's a scenario that'll feel familiar if you've wired up MCP servers for anything beyond a demo.&lt;/p&gt;

&lt;p&gt;You connect GitHub, Slack, and Sentry. Three services, maybe 40 tools total. Before your agent has read a single user message, &lt;a href="https://www.mmntm.net/articles/mcp-context-tax" rel="noopener noreferrer"&gt;55,000 tokens of tool definitions&lt;/a&gt; are sitting in the context window. That's over a quarter of Claude's 200k limit. Gone.&lt;/p&gt;

&lt;p&gt;It gets worse. Each MCP tool costs &lt;a href="https://www.mmntm.net/articles/mcp-context-tax" rel="noopener noreferrer"&gt;550-1,400 tokens&lt;/a&gt; for its name, description, JSON schema, field descriptions, enums, and system instructions. Connect a real API surface, say a SaaS platform with 50+ endpoints, and you're looking at 50,000+ tokens just to describe what the agent &lt;em&gt;could&lt;/em&gt; do, with almost nothing left for what it &lt;em&gt;should&lt;/em&gt; do.&lt;/p&gt;

&lt;p&gt;One team &lt;a href="https://www.agentpmt.com/articles/thousands-of-mcp-tools-zero-context-left-the-bloat-tax-breaking-ai-agents" rel="noopener noreferrer"&gt;reported&lt;/a&gt; three MCP servers consuming 143,000 of 200,000 tokens. That's 72% of the context window burned on tool definitions. The agent had 57,000 tokens left for the actual conversation, retrieved documents, reasoning, and response. Good luck building anything useful in that space.&lt;/p&gt;

&lt;p&gt;This isn't a theoretical concern. David Zhang (&lt;a href="https://x.com/dzhng/status/2029518820872945889" rel="noopener noreferrer"&gt;@dzhng&lt;/a&gt;), building Duet, described ripping out their MCP integrations entirely, even after getting OAuth and dynamic client registration working. The tradeoff was impossible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Load everything up front&lt;/strong&gt; → lose working memory for reasoning and history&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limit integrations&lt;/strong&gt; → agent can only talk to a few services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build dynamic tool loading&lt;/strong&gt; → add latency and middleware complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;He called it a "trilemma."&lt;/p&gt;

&lt;p&gt;And the numbers hold up under controlled testing. A &lt;a href="https://www.scalekit.com/blog/mcp-vs-cli-use" rel="noopener noreferrer"&gt;recent benchmark by Scalekit&lt;/a&gt; ran 75 head-to-head comparisons (same model, Claude Sonnet 4, same tasks, same prompts) and found MCP costing &lt;strong&gt;4 to 32x more tokens&lt;/strong&gt; than CLI for identical operations. Their simplest task, checking a repo's language, consumed 1,365 tokens via CLI and 44,026 via MCP. The overhead is almost entirely schema: 43 tool definitions injected into every conversation, of which the agent uses one or two.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three approaches to the same problem
&lt;/h2&gt;

&lt;p&gt;The industry is converging on three responses to context bloat. Each has a sweet spot.&lt;/p&gt;

&lt;h3&gt;
  
  
  MCP with compression tricks
&lt;/h3&gt;

&lt;p&gt;The first response is to keep MCP but fight the bloat. Teams compress schemas, use tool search to load definitions on demand, or build middleware that slices OpenAPI specs into smaller chunks.&lt;/p&gt;

&lt;p&gt;This works for small, well-defined interactions like looking up an issue, creating a ticket, or fetching a document. MCP's structured tool calls and typed schemas are genuinely useful when you have a tight set of operations that agents use frequently.&lt;/p&gt;

&lt;p&gt;But it adds infrastructure. You need a tool registry, search logic, caching, and routing. You're building a service to manage your services. And you're still paying per-tool token costs every time the agent decides it needs a new capability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code execution
&lt;/h3&gt;

&lt;p&gt;The code execution approach treats the agent like a developer with a persistent workspace. When the agent needs a new integration, it reads the API docs, writes code against the SDK, runs it, and saves the script for reuse. Duet pioneered this pattern by letting agents write and maintain their own integration scripts.&lt;/p&gt;

&lt;p&gt;This is powerful for long-lived workspace agents that maintain state across sessions and need complex workflows: loops, conditionals, polling, batch operations. Things that are awkward to express as individual tool calls become natural in code.&lt;/p&gt;

&lt;p&gt;There's a more targeted variant worth watching: &lt;a href="https://blog.cloudflare.com/code-mode/" rel="noopener noreferrer"&gt;Code Mode&lt;/a&gt;. Instead of writing arbitrary code against raw APIs, the agent writes short orchestration scripts that call structured MCP tools underneath. A &lt;a href="https://www.portofcontext.com/blog/cli-vs-mcp-vs-code-mode" rel="noopener noreferrer"&gt;benchmark by Sideko&lt;/a&gt; across 12 Stripe tasks showed Code Mode MCP using 58% fewer tokens than raw MCP and 56% fewer than CLI. The key insight: on multi-step tasks like creating an invoice with line items, CLI required 19 LLM round trips, raw MCP needed 12, and Code Mode collapsed it to 4. The agent writes a TypeScript program that handles the looping internally, without going back to the LLM at each step.&lt;/p&gt;

&lt;p&gt;This matters because CLI's efficiency advantage, which is real for single-step discovery and reads, can erode on complex chained writes where each round trip compounds context. Code Mode offers a middle ground: structured tool access without the schema bloat, plus the ability to batch operations without per-step LLM overhead.&lt;/p&gt;

&lt;p&gt;The tradeoff is that your agent is writing and executing code against production APIs. Even sandboxed, the safety surface is larger than a CLI with structural permissions. You need review mechanisms and trust in your agent's judgment. But for workflows that involve loops and dependent state, it's a pattern worth considering alongside CLI.&lt;/p&gt;

&lt;h3&gt;
  
  
  CLI as the agent interface
&lt;/h3&gt;

&lt;p&gt;The third approach is the one we took. Instead of loading schemas into the context window or letting the agent write integration code, you give it a CLI.&lt;/p&gt;

&lt;p&gt;A well-designed CLI is a progressive disclosure system by nature. When a human developer needs to use a tool they haven't touched before, they don't read the entire API reference. They run &lt;code&gt;tool --help&lt;/code&gt;, find the subcommand they need, run &lt;code&gt;tool subcommand --help&lt;/code&gt;, and get the specific flags for that operation. They pay attention costs proportional to what they actually need.&lt;/p&gt;

&lt;p&gt;Agents can do exactly the same thing. And the token economics are dramatically different.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why CLIs are the pragmatic sweet spot
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Progressive disclosure saves tokens
&lt;/h3&gt;

&lt;p&gt;Here's what the &lt;a href="https://github.com/apideck-libraries/cli" rel="noopener noreferrer"&gt;Apideck CLI&lt;/a&gt; agent prompt looks like. This is the entire thing an AI agent needs in its system prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use `apideck` to interact with the Apideck Unified API.
Available APIs: `apideck --list`
List resources: `apideck &amp;lt;api&amp;gt; --list`
Operation help: `apideck &amp;lt;api&amp;gt; &amp;lt;resource&amp;gt; &amp;lt;verb&amp;gt; --help`
APIs: accounting, ats, crm, ecommerce, hris, ...
Auth is pre-configured. GET auto-approved. POST/PUT/PATCH prompt (use --yes). DELETE blocked (use --force).
Use --service-id &amp;lt;connector&amp;gt; to target a specific integration.
For clean output: -q -o json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's ~80 tokens. Compare that to the alternatives:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Tokens consumed&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Full OpenAPI spec in context&lt;/td&gt;
&lt;td&gt;30,000-100,000+&lt;/td&gt;
&lt;td&gt;Before first message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP tools (~3,600 per API)&lt;/td&gt;
&lt;td&gt;10,000-50,000+&lt;/td&gt;
&lt;td&gt;Before first message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLI agent prompt&lt;/td&gt;
&lt;td&gt;~80&lt;/td&gt;
&lt;td&gt;Before first message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLI &lt;code&gt;--help&lt;/code&gt; call&lt;/td&gt;
&lt;td&gt;~50-200&lt;/td&gt;
&lt;td&gt;Only when needed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The agent starts with 80 tokens of guidance and discovers capabilities on demand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Level 1: What APIs are available? (~20 tokens output)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;apideck &lt;span class="nt"&gt;--list&lt;/span&gt;
accounting ats connector crm ecommerce hris ...

&lt;span class="c"&gt;# Level 2: What can I do with accounting? (~200 tokens output)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;apideck accounting &lt;span class="nt"&gt;--list&lt;/span&gt;
Resources &lt;span class="k"&gt;in &lt;/span&gt;accounting API:

  invoices
    list       GET  /accounting/invoices
    get        GET  /accounting/invoices/&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
    create     POST /accounting/invoices
    delete     DELETE /accounting/invoices/&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

  customers
    list       GET  /accounting/customers
    ...

&lt;span class="c"&gt;# Level 3: How do I create an invoice? (~150 tokens output)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;apideck accounting invoices create &lt;span class="nt"&gt;--help&lt;/span&gt;
Usage: apideck accounting invoices create &lt;span class="o"&gt;[&lt;/span&gt;flags]

Flags:
  &lt;span class="nt"&gt;--data&lt;/span&gt; string        JSON request body &lt;span class="o"&gt;(&lt;/span&gt;or @file.json&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nt"&gt;--service-id&lt;/span&gt; string  Target a specific connector
  &lt;span class="nt"&gt;--yes&lt;/span&gt;                Skip write confirmation
  &lt;span class="nt"&gt;-o&lt;/span&gt;, &lt;span class="nt"&gt;--output&lt;/span&gt; string  Output format &lt;span class="o"&gt;(&lt;/span&gt;json|table|yaml|csv&lt;span class="o"&gt;)&lt;/span&gt;
  ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each step costs 50-200 tokens, loaded only when the agent decides it needs that information. An agent handling an accounting query might consume 400 tokens total across three &lt;code&gt;--help&lt;/code&gt; calls. The same surface through MCP would cost 10,000+ tokens loaded upfront whether the agent uses them or not.&lt;/p&gt;

&lt;p&gt;This mirrors how &lt;a href="https://www.linkedin.com/posts/lucas-althoff_contextengineering-activity-7401317037458984960-usmb" rel="noopener noreferrer"&gt;Claude Agent Skills&lt;/a&gt; work. Metadata first, full details only when selected, reference material only when needed. The CLI does the same thing through a different mechanism.&lt;/p&gt;

&lt;p&gt;Scalekit's benchmark independently validated this pattern. They found that even a minimal ~800-token "skills file" (a document of CLI tips and common workflows) reduced tool calls by a third and latency by a third compared to a bare CLI. Our approach takes it further: the ~80-token agent prompt provides the same progressive discovery at a tenth of the cost. The principle is the same. A small, upfront hint about how to navigate the tool is worth more than thousands of tokens of exhaustive schema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reliability: local beats remote
&lt;/h3&gt;

&lt;p&gt;There's a dimension of the MCP problem that doesn't get enough attention: &lt;strong&gt;availability&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Scalekit's benchmark recorded a &lt;strong&gt;28% failure rate&lt;/strong&gt; on MCP calls to GitHub's Copilot server. Out of 25 runs, 7 failed with TCP-level connection timeouts. These weren't protocol errors or bad tool calls. The connection never completed.&lt;/p&gt;

&lt;p&gt;CLI agents don't have this failure mode. The binary runs locally. There's no remote server to time out, no connection pool to exhaust. When your agent runs &lt;code&gt;apideck accounting invoices list&lt;/code&gt;, it makes a direct HTTPS call to the Apideck API. One hop, not two.&lt;/p&gt;

&lt;p&gt;This matters at scale. At 10,000 operations per month, a 28% failure rate means roughly 2,800 retries, each burning additional tokens and latency. Scalekit estimated the monthly cost difference at &lt;strong&gt;$3.20 for CLI versus $55.20 for direct MCP&lt;/strong&gt;, a 17x cost multiplier, with the reliability tax on top.&lt;/p&gt;

&lt;p&gt;Remote MCP servers will improve. Connection pooling, better infrastructure, and gateway layers will close the gap. But "the binary is on your machine" is a reliability guarantee that no amount of infrastructure engineering on the server side can match.&lt;/p&gt;

&lt;h3&gt;
  
  
  Structural safety beats prompt-based safety
&lt;/h3&gt;

&lt;p&gt;Telling an agent "never delete production data" in a system prompt is like putting a sticky note on the nuclear launch button. It works until a creative prompt injection peels the note off.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.aikido.dev/blog/promptpwnd-github-actions-ai-agents" rel="noopener noreferrer"&gt;Security research on AI agents in CI/CD&lt;/a&gt; has shown how prompt injection can manipulate agents with high-privilege tokens into leaking secrets or modifying infrastructure. The pattern is always the same: untrusted input gets injected into a prompt, the agent has broad tool access, and bad things happen.&lt;/p&gt;

&lt;p&gt;The Apideck CLI takes a structural approach. Permission classification is baked into the binary based on HTTP method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// From internal/permission/engine.go&lt;/span&gt;
&lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Permission&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PermissionRead&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ActionAllow&lt;/span&gt;      &lt;span class="c"&gt;// GET -&amp;gt; auto-approved&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PermissionWrite&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ActionPrompt&lt;/span&gt;     &lt;span class="c"&gt;// POST/PUT/PATCH -&amp;gt; confirmation required&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PermissionDangerous&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ActionBlock&lt;/span&gt;       &lt;span class="c"&gt;// DELETE -&amp;gt; blocked by default&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No prompt can override this. A &lt;code&gt;DELETE&lt;/code&gt; operation is blocked unless the caller explicitly passes &lt;code&gt;--force&lt;/code&gt;. A &lt;code&gt;POST&lt;/code&gt; requires &lt;code&gt;--yes&lt;/code&gt; or interactive confirmation. &lt;code&gt;GET&lt;/code&gt; operations run freely because they can't modify state.&lt;/p&gt;

&lt;p&gt;The agent frameworks reinforce this. Claude Code, Cursor, and GitHub Copilot all have permission systems that gate shell command execution. So you get two layers of structural safety: the agent framework asks "should I run this command?" and the CLI itself enforces "is this operation allowed?"&lt;/p&gt;

&lt;p&gt;You can also customize the policy per operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/.apideck-cli/permissions.yaml&lt;/span&gt;
&lt;span class="na"&gt;defaults&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;allow&lt;/span&gt;
  &lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prompt&lt;/span&gt;
  &lt;span class="na"&gt;dangerous&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;block&lt;/span&gt;

&lt;span class="na"&gt;overrides&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;accounting.payments.create&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;block&lt;/span&gt;    &lt;span class="c1"&gt;# payments are sensitive&lt;/span&gt;
  &lt;span class="na"&gt;crm.contacts.delete&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prompt&lt;/span&gt;          &lt;span class="c1"&gt;# contacts can be soft-deleted&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the same principle behind &lt;a href="https://blog.duda.co/duda-mcp" rel="noopener noreferrer"&gt;Duda blocking destructive MCP actions&lt;/a&gt;, but enforced structurally in the binary, not through prompt instructions that compete with everything else in the context window.&lt;/p&gt;

&lt;h3&gt;
  
  
  Universal compatibility, zero protocol overhead
&lt;/h3&gt;

&lt;p&gt;Every serious agent framework ships with "run shell command" as a primitive. Claude Code has &lt;code&gt;Bash&lt;/code&gt;. Cursor has terminal access. GitHub Copilot SDK exposes shell execution. Gemini CLI runs commands natively.&lt;/p&gt;

&lt;p&gt;MCP requires dedicated client support, connection plumbing, and server lifecycle management. A CLI requires a binary on the PATH.&lt;/p&gt;

&lt;p&gt;This matters more than it sounds. When you're building an agent that needs to interact with APIs, the integration path for a CLI is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the binary&lt;/li&gt;
&lt;li&gt;Set environment variables for auth&lt;/li&gt;
&lt;li&gt;Add ~80 tokens to the system prompt&lt;/li&gt;
&lt;li&gt;Done&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The integration path for MCP is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Implement or configure an MCP client&lt;/li&gt;
&lt;li&gt;Set up server connections (transport, auth, lifecycle)&lt;/li&gt;
&lt;li&gt;Handle tool registration and schema loading&lt;/li&gt;
&lt;li&gt;Manage connection state and reconnection&lt;/li&gt;
&lt;li&gt;Deal with the token budget for tool definitions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The CLI approach also means your agent integration isn't locked to any specific framework. The same &lt;code&gt;apideck&lt;/code&gt; binary works from Claude Code, Cursor, a custom Python agent, a bash script, or a CI/CD pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we built it
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/apideck-libraries/cli" rel="noopener noreferrer"&gt;Apideck CLI&lt;/a&gt; is a single static binary that parses our OpenAPI spec at startup and generates its entire command tree dynamically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenAPI-native, no code generation.&lt;/strong&gt; The binary embeds the latest Apideck Unified API spec. On startup, it parses the spec with &lt;a href="https://github.com/pb33f/libopenapi" rel="noopener noreferrer"&gt;libopenapi&lt;/a&gt; and builds commands for every API group, resource, and operation. When the API adds new endpoints, &lt;code&gt;apideck sync&lt;/code&gt; pulls the latest spec. No SDK regeneration, no version bumps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Smart output defaults.&lt;/strong&gt; When running in a terminal, output defaults to a formatted table with colors. When piped or called from a non-TTY (which is how agents call it), output defaults to JSON. Agents get machine-parseable output without needing to remember &lt;code&gt;--output json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Agent calls this (non-TTY) -&amp;gt; gets JSON automatically&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;apideck accounting invoices list &lt;span class="nt"&gt;-q&lt;/span&gt;
&lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;: &lt;span class="s2"&gt;"inv_12345"&lt;/span&gt;, &lt;span class="s2"&gt;"number"&lt;/span&gt;: &lt;span class="s2"&gt;"INV-001"&lt;/span&gt;, &lt;span class="s2"&gt;"total"&lt;/span&gt;: 1500.00, ...&lt;span class="o"&gt;}]&lt;/span&gt;

&lt;span class="c"&gt;# Human runs the same command in terminal -&amp;gt; gets a table&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;apideck accounting invoices list
┌──────────┬─────────┬──────────┐
│ ID       │ Number  │ Total    │
├──────────┼─────────┼──────────┤
│ inv_12345│ INV-001 │ 1,500.00 │
└──────────┴─────────┴──────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Auth is invisible.&lt;/strong&gt; Credentials are resolved from environment variables (&lt;code&gt;APIDECK_API_KEY&lt;/code&gt;, &lt;code&gt;APIDECK_APP_ID&lt;/code&gt;, &lt;code&gt;APIDECK_CONSUMER_ID&lt;/code&gt;) or a config file, and injected into every request automatically. The agent never handles tokens, never sees auth headers, never needs to manage sessions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connector targeting.&lt;/strong&gt; The &lt;code&gt;--service-id&lt;/code&gt; flag lets agents target specific integrations. &lt;code&gt;apideck accounting invoices list --service-id quickbooks&lt;/code&gt; hits QuickBooks. Swap to &lt;code&gt;--service-id xero&lt;/code&gt; and the same command hits Xero. Same interface, different backend. The unified API handles the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  When CLI isn't the answer
&lt;/h2&gt;

&lt;p&gt;CLIs aren't universally better. Here's where the other approaches win.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MCP is better for tightly scoped, high-frequency tools.&lt;/strong&gt; If your agent calls the same 5-10 tools hundreds of times per session, the upfront schema cost amortizes well. A customer support agent that only ever looks up tickets, updates status, and sends replies doesn't need progressive disclosure. It needs those tools ready immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code execution is better for complex, stateful workflows.&lt;/strong&gt; If your agent needs to poll an API every 30 seconds, aggregate results across paginated endpoints, or orchestrate multi-step transactions with rollback logic, writing code is more natural than chaining CLI calls. As the Sideko benchmark showed, CLI's efficiency advantage can reverse on multi-step chained writes where each round trip compounds context. For those patterns, Code Mode (agent writes orchestration scripts that call structured tools) or the Duet approach (full code execution) will use fewer total tokens despite higher per-step overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MCP is better when your agent acts on behalf of other people's users.&lt;/strong&gt; This is the dimension most CLI-vs-MCP comparisons gloss over, and it's worth being direct about. When your agent automates &lt;em&gt;your own&lt;/em&gt; workflow, ambient credentials are fine. You are the user, and the only person at risk is you. But if you're building a B2B product where agents act on behalf of your customers' employees, across organizations those customers control, the identity problem becomes three-layered: which agent is calling, which user authorized it, and which tenant's data boundary applies. Per-user OAuth with scoped access, consent flows, and structured audit trails are real requirements at that boundary, and they're requirements that raw CLI auth (&lt;code&gt;gh auth login&lt;/code&gt;, environment variables) wasn't designed to solve. MCP's authorization model, whatever its efficiency cost, addresses this natively.&lt;/p&gt;

&lt;p&gt;There's also a deeper identity gap that CLI auth doesn't address: agent-level identity. A CLI token authenticates the user, but the API provider never knows &lt;em&gt;which agent&lt;/em&gt; made the request. That matters for policy enforcement. If an API provider has a partnership with Agent A but not Agent B, there's no way to distinguish them in a CLI token. MCP's OAuth model can carry agent identity through claims like &lt;code&gt;act&lt;/code&gt;, which becomes critical as agents start calling other agents and you need the full chain of delegation in the token. For single-agent workflows this is academic. For multi-agent architectures it's a real architectural constraint.&lt;/p&gt;

&lt;p&gt;That said, the gap is narrower than it looks for unified API architectures. Apideck already centralizes auth through &lt;a href="https://www.apideck.com/products/vault" rel="noopener noreferrer"&gt;Vault&lt;/a&gt;: credentials are managed per-consumer, per-connection, and scoped by service. The &lt;code&gt;--service-id&lt;/code&gt; flag targets a specific integration within a specific consumer's vault. The structural permission system enforces read/write/delete boundaries in the binary. What's missing is the per-user OAuth consent flow and tenant-scoped audit trail, real gaps, but ones that sit at the platform layer, not the agent interface layer. A CLI can be the interface while a backend handles delegated authorization. These aren't mutually exclusive.&lt;/p&gt;

&lt;p&gt;It's also worth noting that MCP's auth story is less settled than it appears. As &lt;a href="https://www.speakeasy.com/docs/mcp-platform/secure/add-oauth-to-mcp-servers" rel="noopener noreferrer"&gt;Speakeasy's MCP OAuth guide&lt;/a&gt; makes clear, user-facing OAuth exchange is not actually required by the MCP spec. Passing access tokens or API keys directly is completely valid. The real complexity kicks in when MCP clients need to handle OAuth flows dynamically, which requires Dynamic Client Registration (DCR), a capability most API providers don't support today. Companies like Stripe and Asana have started adding DCR to accommodate MCP, but it remains a high-friction integration. The auth advantage MCP has over CLI is real in theory, but in practice, the ecosystem is still catching up to the spec.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLIs are weaker at streaming and bi-directional communication.&lt;/strong&gt; A CLI call is request-response. If you need server-sent events, WebSocket streams, or long-lived connections, you'll want an SDK or MCP server that can hold a connection open.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Distribution has friction.&lt;/strong&gt; MCP servers can theoretically live behind a URL. CLIs need a binary per platform, updates, and PATH management. For the Apideck CLI, we ship a static Go binary that runs everywhere without dependencies, but it's still a binary you need to install.&lt;/p&gt;

&lt;p&gt;The honest framing: MCP, code execution, and CLIs are complementary tools. The mistake is treating MCP as the universal answer when, for many integration patterns, a CLI does the job with two orders of magnitude less context overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for API providers
&lt;/h2&gt;

&lt;p&gt;If you're building developer tools in 2026, AI agents are becoming a primary consumer of your API surface. Not the only consumer (human developers still matter), but a rapidly growing one.&lt;/p&gt;

&lt;p&gt;A few things are worth considering:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your OpenAPI spec is too big for a context window.&lt;/strong&gt; If you have 50+ endpoints, converting your spec to MCP tools will burn the budget of most agent interactions. Think about what a minimal entry point looks like.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Progressive disclosure isn't just a UX pattern anymore.&lt;/strong&gt; It's a token optimization strategy. Give agents a way to discover capabilities incrementally instead of dumping everything upfront.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structural safety is non-negotiable.&lt;/strong&gt; Prompt-based guardrails are the security equivalent of honor system parking. Build permission models into your tools, not your prompts. Classify operations by risk level and enforce that classification in code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ship machine-friendly output formats.&lt;/strong&gt; JSON by default in non-interactive contexts. Stable exit codes. Deterministic output. These are &lt;a href="https://dev.to/tumf/agentic-cli-design-7-principles-for-designing-cli-as-a-protocol-for-ai-agents-2c10"&gt;documented principles for agentic CLI design&lt;/a&gt;, and they matter because your next power user might not have hands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.apideck.com/blog/mcp-vs-api" rel="noopener noreferrer"&gt;MCP vs API&lt;/a&gt; - How MCP and REST APIs relate (Apideck blog)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.apideck.com/blog/api-design-principles-agentic-era" rel="noopener noreferrer"&gt;API Design Principles for the Agentic Era&lt;/a&gt; - Designing APIs with AI agents as first-class consumers&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.apideck.com/blog/understanding-the-security-landscape-of-mcp" rel="noopener noreferrer"&gt;Understanding the Security Landscape of MCP&lt;/a&gt; - MCP security considerations in depth&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.mmntm.net/articles/mcp-context-tax" rel="noopener noreferrer"&gt;The MCP Context Tax&lt;/a&gt; - Detailed analysis of MCP token overhead&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/tumf/agentic-cli-design-7-principles-for-designing-cli-as-a-protocol-for-ai-agents-2c10"&gt;Agentic CLI Design: 7 Principles&lt;/a&gt; - Design principles for CLIs as agent interfaces&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.scalekit.com/blog/mcp-vs-cli-use" rel="noopener noreferrer"&gt;MCP vs CLI Benchmark&lt;/a&gt; - Scalekit's head-to-head benchmark data (75 runs, Claude Sonnet 4)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.portofcontext.com/blog/cli-vs-mcp-vs-code-mode" rel="noopener noreferrer"&gt;CLI vs MCP vs Code Mode Benchmark&lt;/a&gt; - Sideko's 12-task Stripe benchmark comparing all three approaches&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.cloudflare.com/code-mode/" rel="noopener noreferrer"&gt;Code Mode: the better way to use MCP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.atcyrus.com/stories/mcp-tool-search-claude-code-context-pollution-guide" rel="noopener noreferrer"&gt;What is MCP Tool Search?&lt;/a&gt; - The Claude Code feature that addresses context pollution&lt;/li&gt;
&lt;li&gt;&lt;a href="https://x.com/yenkel/status/2032098351567487037" rel="noopener noreferrer"&gt;If you kill MCP, you don't give a s**t about security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://portofcontext.com/blog/cli-vs-mcp-vs-code-mode" rel="noopener noreferrer"&gt;CLI vs. MCP vs. Code Mode&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>cli</category>
      <category>api</category>
    </item>
    <item>
      <title>Your MCP Server Is Eating Your Context Window. There's a Simpler Way</title>
      <dc:creator>samz</dc:creator>
      <pubDate>Mon, 16 Mar 2026 15:16:26 +0000</pubDate>
      <link>https://dev.to/amzani/your-mcp-server-is-eating-your-context-window-theres-a-simpler-way-3ja2</link>
      <guid>https://dev.to/amzani/your-mcp-server-is-eating-your-context-window-theres-a-simpler-way-3ja2</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; MCP tool definitions can burn 55,000+ tokens before an agent processes a single user message. We built the Apideck CLI as an AI-agent interface instead:an ~80-token agent prompt replaces tens of thousands of tokens of schema, with progressive disclosure via &lt;code&gt;--help&lt;/code&gt; and structural safety baked into the binary. Any agent that can run shell commands can use it. No protocol support required.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem demos never show you
&lt;/h2&gt;

&lt;p&gt;Here's a scenario that'll feel familiar if you've wired up MCP servers for anything beyond a demo.&lt;/p&gt;

&lt;p&gt;You connect GitHub, Slack, and Sentry. Three services, maybe 40 tools total. Before your agent has read a single user message, &lt;a href="https://www.mmntm.net/articles/mcp-context-tax" rel="noopener noreferrer"&gt;55,000 tokens of tool definitions&lt;/a&gt; are sitting in the context window. That's over a quarter of Claude's 200k limit. Gone.&lt;/p&gt;

&lt;p&gt;It gets worse. Each MCP tool costs &lt;a href="https://www.mmntm.net/articles/mcp-context-tax" rel="noopener noreferrer"&gt;550-1,400 tokens&lt;/a&gt; for its name, description, JSON schema, field descriptions, enums, and system instructions. Connect a real API surface, say a SaaS platform with 50+ endpoints, and you're looking at 50,000+ tokens just to describe what the agent &lt;em&gt;could&lt;/em&gt; do, with almost nothing left for what it &lt;em&gt;should&lt;/em&gt; do.&lt;/p&gt;

&lt;p&gt;One team &lt;a href="https://www.agentpmt.com/articles/thousands-of-mcp-tools-zero-context-left-the-bloat-tax-breaking-ai-agents" rel="noopener noreferrer"&gt;reported&lt;/a&gt; three MCP servers consuming 143,000 of 200,000 tokens. That's 72% of the context window burned on tool definitions. The agent had 57,000 tokens left for the actual conversation, retrieved documents, reasoning, and response. Good luck building anything useful in that space.&lt;/p&gt;

&lt;p&gt;This isn't a theoretical concern. David Zhang (&lt;a href="https://x.com/dzhng/status/2029518820872945889" rel="noopener noreferrer"&gt;@dzhng&lt;/a&gt;), building Duet, described ripping out their MCP integrations entirely, even after getting OAuth and dynamic client registration working. The tradeoff was impossible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Load everything up front&lt;/strong&gt; → lose working memory for reasoning and history&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limit integrations&lt;/strong&gt; → agent can only talk to a few services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build dynamic tool loading&lt;/strong&gt; → add latency and middleware complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;He called it a "trilemma."&lt;/p&gt;

&lt;p&gt;And the numbers hold up under controlled testing. A &lt;a href="https://www.scalekit.com/blog/mcp-vs-cli-use" rel="noopener noreferrer"&gt;recent benchmark by Scalekit&lt;/a&gt; ran 75 head-to-head comparisons (same model, Claude Sonnet 4, same tasks, same prompts) and found MCP costing &lt;strong&gt;4 to 32x more tokens&lt;/strong&gt; than CLI for identical operations. Their simplest task, checking a repo's language, consumed 1,365 tokens via CLI and 44,026 via MCP. The overhead is almost entirely schema: 43 tool definitions injected into every conversation, of which the agent uses one or two.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three approaches to the same problem
&lt;/h2&gt;

&lt;p&gt;The industry is converging on three responses to context bloat. Each has a sweet spot.&lt;/p&gt;

&lt;h3&gt;
  
  
  MCP with compression tricks
&lt;/h3&gt;

&lt;p&gt;The first response is to keep MCP but fight the bloat. Teams compress schemas, use tool search to load definitions on demand, or build middleware that slices OpenAPI specs into smaller chunks.&lt;/p&gt;

&lt;p&gt;This works for small, well-defined interactions like looking up an issue, creating a ticket, or fetching a document. MCP's structured tool calls and typed schemas are genuinely useful when you have a tight set of operations that agents use frequently.&lt;/p&gt;

&lt;p&gt;But it adds infrastructure. You need a tool registry, search logic, caching, and routing. You're building a service to manage your services. And you're still paying per-tool token costs every time the agent decides it needs a new capability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code execution
&lt;/h3&gt;

&lt;p&gt;The code execution approach treats the agent like a developer with a persistent workspace. When the agent needs a new integration, it reads the API docs, writes code against the SDK, runs it, and saves the script for reuse. Duet pioneered this pattern by letting agents write and maintain their own integration scripts.&lt;/p&gt;

&lt;p&gt;This is powerful for long-lived workspace agents that maintain state across sessions and need complex workflows: loops, conditionals, polling, batch operations. Things that are awkward to express as individual tool calls become natural in code.&lt;/p&gt;

&lt;p&gt;There's a more targeted variant worth watching: &lt;a href="https://blog.cloudflare.com/code-mode/" rel="noopener noreferrer"&gt;Code Mode&lt;/a&gt;. Instead of writing arbitrary code against raw APIs, the agent writes short orchestration scripts that call structured MCP tools underneath. A &lt;a href="https://www.portofcontext.com/blog/cli-vs-mcp-vs-code-mode" rel="noopener noreferrer"&gt;benchmark by Sideko&lt;/a&gt; across 12 Stripe tasks showed Code Mode MCP using 58% fewer tokens than raw MCP and 56% fewer than CLI. The key insight: on multi-step tasks like creating an invoice with line items, CLI required 19 LLM round trips, raw MCP needed 12, and Code Mode collapsed it to 4. The agent writes a TypeScript program that handles the looping internally, without going back to the LLM at each step.&lt;/p&gt;

&lt;p&gt;This matters because CLI's efficiency advantage, which is real for single-step discovery and reads, can erode on complex chained writes where each round trip compounds context. Code Mode offers a middle ground: structured tool access without the schema bloat, plus the ability to batch operations without per-step LLM overhead.&lt;/p&gt;

&lt;p&gt;The tradeoff is that your agent is writing and executing code against production APIs. Even sandboxed, the safety surface is larger than a CLI with structural permissions. You need review mechanisms and trust in your agent's judgment. But for workflows that involve loops and dependent state, it's a pattern worth considering alongside CLI.&lt;/p&gt;

&lt;h3&gt;
  
  
  CLI as the agent interface
&lt;/h3&gt;

&lt;p&gt;The third approach is the one we took. Instead of loading schemas into the context window or letting the agent write integration code, you give it a CLI.&lt;/p&gt;

&lt;p&gt;A well-designed CLI is a progressive disclosure system by nature. When a human developer needs to use a tool they haven't touched before, they don't read the entire API reference. They run &lt;code&gt;tool --help&lt;/code&gt;, find the subcommand they need, run &lt;code&gt;tool subcommand --help&lt;/code&gt;, and get the specific flags for that operation. They pay attention costs proportional to what they actually need.&lt;/p&gt;

&lt;p&gt;Agents can do exactly the same thing. And the token economics are dramatically different.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why CLIs are the pragmatic sweet spot
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Progressive disclosure saves tokens
&lt;/h3&gt;

&lt;p&gt;Here's what the &lt;a href="https://github.com/apideck-libraries/cli" rel="noopener noreferrer"&gt;Apideck CLI&lt;/a&gt; agent prompt looks like. This is the entire thing an AI agent needs in its system prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Use &lt;span class="sb"&gt;`&lt;/span&gt;apideck&lt;span class="sb"&gt;`&lt;/span&gt; to interact with the Apideck Unified API.
Available APIs: &lt;span class="sb"&gt;`&lt;/span&gt;apideck &lt;span class="nt"&gt;--list&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
List resources: &lt;span class="sb"&gt;`&lt;/span&gt;apideck &amp;lt;api&amp;gt; &lt;span class="nt"&gt;--list&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
Operation &lt;span class="nb"&gt;help&lt;/span&gt;: &lt;span class="sb"&gt;`&lt;/span&gt;apideck &amp;lt;api&amp;gt; &amp;lt;resource&amp;gt; &amp;lt;verb&amp;gt; &lt;span class="nt"&gt;--help&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
APIs: accounting, ats, crm, ecommerce, hris, ...
Auth is pre-configured. GET auto-approved. POST/PUT/PATCH prompt &lt;span class="o"&gt;(&lt;/span&gt;use &lt;span class="nt"&gt;--yes&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; DELETE blocked &lt;span class="o"&gt;(&lt;/span&gt;use &lt;span class="nt"&gt;--force&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Use &lt;span class="nt"&gt;--service-id&lt;/span&gt; &amp;lt;connector&amp;gt; to target a specific integration.
For clean output: &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's ~80 tokens. Compare that to the alternatives:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Tokens consumed&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Full OpenAPI spec in context&lt;/td&gt;
&lt;td&gt;30,000-100,000+&lt;/td&gt;
&lt;td&gt;Before first message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP tools (~3,600 per API)&lt;/td&gt;
&lt;td&gt;10,000-50,000+&lt;/td&gt;
&lt;td&gt;Before first message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLI agent prompt&lt;/td&gt;
&lt;td&gt;~80&lt;/td&gt;
&lt;td&gt;Before first message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLI &lt;code&gt;--help&lt;/code&gt; call&lt;/td&gt;
&lt;td&gt;~50-200&lt;/td&gt;
&lt;td&gt;Only when needed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The agent starts with 80 tokens of guidance and discovers capabilities on demand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Level 1: What APIs are available? (~20 tokens output)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;apideck &lt;span class="nt"&gt;--list&lt;/span&gt;
accounting ats connector crm ecommerce hris ...

&lt;span class="c"&gt;# Level 2: What can I do with accounting? (~200 tokens output)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;apideck accounting &lt;span class="nt"&gt;--list&lt;/span&gt;
Resources &lt;span class="k"&gt;in &lt;/span&gt;accounting API:

  invoices
    list       GET  /accounting/invoices
    get        GET  /accounting/invoices/&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
    create     POST /accounting/invoices
    delete     DELETE /accounting/invoices/&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

  customers
    list       GET  /accounting/customers
    ...

&lt;span class="c"&gt;# Level 3: How do I create an invoice? (~150 tokens output)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;apideck accounting invoices create &lt;span class="nt"&gt;--help&lt;/span&gt;
Usage: apideck accounting invoices create &lt;span class="o"&gt;[&lt;/span&gt;flags]

Flags:
  &lt;span class="nt"&gt;--data&lt;/span&gt; string        JSON request body &lt;span class="o"&gt;(&lt;/span&gt;or @file.json&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nt"&gt;--service-id&lt;/span&gt; string  Target a specific connector
  &lt;span class="nt"&gt;--yes&lt;/span&gt;                Skip write confirmation
  &lt;span class="nt"&gt;-o&lt;/span&gt;, &lt;span class="nt"&gt;--output&lt;/span&gt; string  Output format &lt;span class="o"&gt;(&lt;/span&gt;json|table|yaml|csv&lt;span class="o"&gt;)&lt;/span&gt;
  ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each step costs 50-200 tokens, loaded only when the agent decides it needs that information. An agent handling an accounting query might consume 400 tokens total across three &lt;code&gt;--help&lt;/code&gt; calls. The same surface through MCP would cost 10,000+ tokens loaded upfront whether the agent uses them or not.&lt;/p&gt;

&lt;p&gt;This mirrors how &lt;a href="https://www.linkedin.com/posts/lucas-althoff_contextengineering-activity-7401317037458984960-usmb" rel="noopener noreferrer"&gt;Claude Agent Skills&lt;/a&gt; work. Metadata first, full details only when selected, reference material only when needed. The CLI does the same thing through a different mechanism.&lt;/p&gt;

&lt;p&gt;Scalekit's benchmark independently validated this pattern. They found that even a minimal ~800-token "skills file" (a document of CLI tips and common workflows) reduced tool calls by a third and latency by a third compared to a bare CLI. Our approach takes it further: the ~80-token agent prompt provides the same progressive discovery at a tenth of the cost. The principle is the same. A small, upfront hint about how to navigate the tool is worth more than thousands of tokens of exhaustive schema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reliability: local beats remote
&lt;/h3&gt;

&lt;p&gt;There's a dimension of the MCP problem that doesn't get enough attention: &lt;strong&gt;availability&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Scalekit's benchmark recorded a &lt;strong&gt;28% failure rate&lt;/strong&gt; on MCP calls to GitHub's Copilot server. Out of 25 runs, 7 failed with TCP-level connection timeouts. These weren't protocol errors or bad tool calls. The connection never completed.&lt;/p&gt;

&lt;p&gt;CLI agents don't have this failure mode. The binary runs locally. There's no remote server to time out, no connection pool to exhaust. When your agent runs &lt;code&gt;apideck accounting invoices list&lt;/code&gt;, it makes a direct HTTPS call to the Apideck API. One hop, not two.&lt;/p&gt;

&lt;p&gt;This matters at scale. At 10,000 operations per month, a 28% failure rate means roughly 2,800 retries, each burning additional tokens and latency. Scalekit estimated the monthly cost difference at &lt;strong&gt;$3.20 for CLI versus $55.20 for direct MCP&lt;/strong&gt;, a 17x cost multiplier, with the reliability tax on top.&lt;/p&gt;

&lt;p&gt;Remote MCP servers will improve. Connection pooling, better infrastructure, and gateway layers will close the gap. But "the binary is on your machine" is a reliability guarantee that no amount of infrastructure engineering on the server side can match.&lt;/p&gt;

&lt;h3&gt;
  
  
  Structural safety beats prompt-based safety
&lt;/h3&gt;

&lt;p&gt;Telling an agent "never delete production data" in a system prompt is like putting a sticky note on the nuclear launch button. It works until a creative prompt injection peels the note off.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.aikido.dev/blog/promptpwnd-github-actions-ai-agents" rel="noopener noreferrer"&gt;Security research on AI agents in CI/CD&lt;/a&gt; has shown how prompt injection can manipulate agents with high-privilege tokens into leaking secrets or modifying infrastructure. The pattern is always the same: untrusted input gets injected into a prompt, the agent has broad tool access, and bad things happen.&lt;/p&gt;

&lt;p&gt;The Apideck CLI takes a structural approach. Permission classification is baked into the binary based on HTTP method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// From internal/permission/engine.go&lt;/span&gt;
&lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Permission&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PermissionRead&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ActionAllow&lt;/span&gt;      &lt;span class="c"&gt;// GET -&amp;gt; auto-approved&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PermissionWrite&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ActionPrompt&lt;/span&gt;     &lt;span class="c"&gt;// POST/PUT/PATCH -&amp;gt; confirmation required&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PermissionDangerous&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ActionBlock&lt;/span&gt;       &lt;span class="c"&gt;// DELETE -&amp;gt; blocked by default&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No prompt can override this. A &lt;code&gt;DELETE&lt;/code&gt; operation is blocked unless the caller explicitly passes &lt;code&gt;--force&lt;/code&gt;. A &lt;code&gt;POST&lt;/code&gt; requires &lt;code&gt;--yes&lt;/code&gt; or interactive confirmation. &lt;code&gt;GET&lt;/code&gt; operations run freely because they can't modify state.&lt;/p&gt;

&lt;p&gt;The agent frameworks reinforce this. Claude Code, Cursor, and GitHub Copilot all have permission systems that gate shell command execution. So you get two layers of structural safety: the agent framework asks "should I run this command?" and the CLI itself enforces "is this operation allowed?"&lt;/p&gt;

&lt;p&gt;You can also customize the policy per operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/.apideck-cli/permissions.yaml&lt;/span&gt;
&lt;span class="na"&gt;defaults&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;allow&lt;/span&gt;
  &lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prompt&lt;/span&gt;
  &lt;span class="na"&gt;dangerous&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;block&lt;/span&gt;

&lt;span class="na"&gt;overrides&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;accounting.payments.create&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;block&lt;/span&gt;    &lt;span class="c1"&gt;# payments are sensitive&lt;/span&gt;
  &lt;span class="na"&gt;crm.contacts.delete&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prompt&lt;/span&gt;          &lt;span class="c1"&gt;# contacts can be soft-deleted&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the same principle behind &lt;a href="https://blog.duda.co/duda-mcp" rel="noopener noreferrer"&gt;Duda blocking destructive MCP actions&lt;/a&gt;, but enforced structurally in the binary, not through prompt instructions that compete with everything else in the context window.&lt;/p&gt;

&lt;h3&gt;
  
  
  Universal compatibility, zero protocol overhead
&lt;/h3&gt;

&lt;p&gt;Every serious agent framework ships with "run shell command" as a primitive. Claude Code has &lt;code&gt;Bash&lt;/code&gt;. Cursor has terminal access. GitHub Copilot SDK exposes shell execution. Gemini CLI runs commands natively.&lt;/p&gt;

&lt;p&gt;MCP requires dedicated client support, connection plumbing, and server lifecycle management. A CLI requires a binary on the PATH.&lt;/p&gt;

&lt;p&gt;This matters more than it sounds. When you're building an agent that needs to interact with APIs, the integration path for a CLI is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the binary&lt;/li&gt;
&lt;li&gt;Set environment variables for auth&lt;/li&gt;
&lt;li&gt;Add ~80 tokens to the system prompt&lt;/li&gt;
&lt;li&gt;Done&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The integration path for MCP is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Implement or configure an MCP client&lt;/li&gt;
&lt;li&gt;Set up server connections (transport, auth, lifecycle)&lt;/li&gt;
&lt;li&gt;Handle tool registration and schema loading&lt;/li&gt;
&lt;li&gt;Manage connection state and reconnection&lt;/li&gt;
&lt;li&gt;Deal with the token budget for tool definitions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The CLI approach also means your agent integration isn't locked to any specific framework. The same &lt;code&gt;apideck&lt;/code&gt; binary works from Claude Code, Cursor, a custom Python agent, a bash script, or a CI/CD pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we built it
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/apideck-libraries/cli" rel="noopener noreferrer"&gt;Apideck CLI&lt;/a&gt; is a single static binary that parses our OpenAPI spec at startup and generates its entire command tree dynamically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenAPI-native, no code generation.&lt;/strong&gt; The binary embeds the latest Apideck Unified API spec. On startup, it parses the spec with &lt;a href="https://github.com/pb33f/libopenapi" rel="noopener noreferrer"&gt;libopenapi&lt;/a&gt; and builds commands for every API group, resource, and operation. When the API adds new endpoints, &lt;code&gt;apideck sync&lt;/code&gt; pulls the latest spec. No SDK regeneration, no version bumps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Smart output defaults.&lt;/strong&gt; When running in a terminal, output defaults to a formatted table with colors. When piped or called from a non-TTY (which is how agents call it), output defaults to JSON. Agents get machine-parseable output without needing to remember &lt;code&gt;--output json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Agent calls this (non-TTY) -&amp;gt; gets JSON automatically&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;apideck accounting invoices list &lt;span class="nt"&gt;-q&lt;/span&gt;
&lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;: &lt;span class="s2"&gt;"inv_12345"&lt;/span&gt;, &lt;span class="s2"&gt;"number"&lt;/span&gt;: &lt;span class="s2"&gt;"INV-001"&lt;/span&gt;, &lt;span class="s2"&gt;"total"&lt;/span&gt;: 1500.00, ...&lt;span class="o"&gt;}]&lt;/span&gt;

&lt;span class="c"&gt;# Human runs the same command in terminal -&amp;gt; gets a table&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;apideck accounting invoices list
┌──────────┬─────────┬──────────┐
│ ID       │ Number  │ Total    │
├──────────┼─────────┼──────────┤
│ inv_12345│ INV-001 │ 1,500.00 │
└──────────┴─────────┴──────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Auth is invisible.&lt;/strong&gt; Credentials are resolved from environment variables (&lt;code&gt;APIDECK_API_KEY&lt;/code&gt;, &lt;code&gt;APIDECK_APP_ID&lt;/code&gt;, &lt;code&gt;APIDECK_CONSUMER_ID&lt;/code&gt;) or a config file, and injected into every request automatically. The agent never handles tokens, never sees auth headers, never needs to manage sessions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connector targeting.&lt;/strong&gt; The &lt;code&gt;--service-id&lt;/code&gt; flag lets agents target specific integrations. &lt;code&gt;apideck accounting invoices list --service-id quickbooks&lt;/code&gt; hits QuickBooks. Swap to &lt;code&gt;--service-id xero&lt;/code&gt; and the same command hits Xero. Same interface, different backend. The unified API handles the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  When CLI isn't the answer
&lt;/h2&gt;

&lt;p&gt;CLIs aren't universally better. Here's where the other approaches win.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MCP is better for tightly scoped, high-frequency tools.&lt;/strong&gt; If your agent calls the same 5-10 tools hundreds of times per session, the upfront schema cost amortizes well. A customer support agent that only ever looks up tickets, updates status, and sends replies doesn't need progressive disclosure. It needs those tools ready immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code execution is better for complex, stateful workflows.&lt;/strong&gt; If your agent needs to poll an API every 30 seconds, aggregate results across paginated endpoints, or orchestrate multi-step transactions with rollback logic, writing code is more natural than chaining CLI calls. As the Sideko benchmark showed, CLI's efficiency advantage can reverse on multi-step chained writes where each round trip compounds context. For those patterns, Code Mode (agent writes orchestration scripts that call structured tools) or the Duet approach (full code execution) will use fewer total tokens despite higher per-step overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MCP is better when your agent acts on behalf of other people's users.&lt;/strong&gt; This is the dimension most CLI-vs-MCP comparisons gloss over, and it's worth being direct about. When your agent automates &lt;em&gt;your own&lt;/em&gt; workflow, ambient credentials are fine. You are the user, and the only person at risk is you. But if you're building a B2B product where agents act on behalf of your customers' employees, across organizations those customers control, the identity problem becomes three-layered: which agent is calling, which user authorized it, and which tenant's data boundary applies. Per-user OAuth with scoped access, consent flows, and structured audit trails are real requirements at that boundary, and they're requirements that raw CLI auth (&lt;code&gt;gh auth login&lt;/code&gt;, environment variables) wasn't designed to solve. MCP's authorization model, whatever its efficiency cost, addresses this natively.&lt;/p&gt;

&lt;p&gt;There's also a deeper identity gap that CLI auth doesn't address: agent-level identity. A CLI token authenticates the user, but the API provider never knows &lt;em&gt;which agent&lt;/em&gt; made the request. That matters for policy enforcement. If an API provider has a partnership with Agent A but not Agent B, there's no way to distinguish them in a CLI token. MCP's OAuth model can carry agent identity through claims like &lt;code&gt;act&lt;/code&gt;, which becomes critical as agents start calling other agents and you need the full chain of delegation in the token. For single-agent workflows this is academic. For multi-agent architectures it's a real architectural constraint.&lt;/p&gt;

&lt;p&gt;That said, the gap is narrower than it looks for unified API architectures. Apideck already centralizes auth through &lt;a href="https://www.apideck.com/products/vault" rel="noopener noreferrer"&gt;Vault&lt;/a&gt;: credentials are managed per-consumer, per-connection, and scoped by service. The &lt;code&gt;--service-id&lt;/code&gt; flag targets a specific integration within a specific consumer's vault. The structural permission system enforces read/write/delete boundaries in the binary. What's missing is the per-user OAuth consent flow and tenant-scoped audit trail, real gaps, but ones that sit at the platform layer, not the agent interface layer. A CLI can be the interface while a backend handles delegated authorization. These aren't mutually exclusive.&lt;/p&gt;

&lt;p&gt;It's also worth noting that MCP's auth story is less settled than it appears. As &lt;a href="https://www.speakeasy.com/docs/mcp-platform/secure/add-oauth-to-mcp-servers" rel="noopener noreferrer"&gt;Speakeasy's MCP OAuth guide&lt;/a&gt; makes clear, user-facing OAuth exchange is not actually required by the MCP spec. Passing access tokens or API keys directly is completely valid. The real complexity kicks in when MCP clients need to handle OAuth flows dynamically, which requires Dynamic Client Registration (DCR), a capability most API providers don't support today. Companies like Stripe and Asana have started adding DCR to accommodate MCP, but it remains a high-friction integration. The auth advantage MCP has over CLI is real in theory, but in practice, the ecosystem is still catching up to the spec.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLIs are weaker at streaming and bi-directional communication.&lt;/strong&gt; A CLI call is request-response. If you need server-sent events, WebSocket streams, or long-lived connections, you'll want an SDK or MCP server that can hold a connection open.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Distribution has friction.&lt;/strong&gt; MCP servers can theoretically live behind a URL. CLIs need a binary per platform, updates, and PATH management. For the Apideck CLI, we ship a static Go binary that runs everywhere without dependencies, but it's still a binary you need to install.&lt;/p&gt;

&lt;p&gt;The honest framing: MCP, code execution, and CLIs are complementary tools. The mistake is treating MCP as the universal answer when, for many integration patterns, a CLI does the job with two orders of magnitude less context overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for API providers
&lt;/h2&gt;

&lt;p&gt;If you're building developer tools in 2026, AI agents are becoming a primary consumer of your API surface. Not the only consumer (human developers still matter), but a rapidly growing one.&lt;/p&gt;

&lt;p&gt;A few things are worth considering:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your OpenAPI spec is too big for a context window.&lt;/strong&gt; If you have 50+ endpoints, converting your spec to MCP tools will burn the budget of most agent interactions. Think about what a minimal entry point looks like.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Progressive disclosure isn't just a UX pattern anymore.&lt;/strong&gt; It's a token optimization strategy. Give agents a way to discover capabilities incrementally instead of dumping everything upfront.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structural safety is non-negotiable.&lt;/strong&gt; Prompt-based guardrails are the security equivalent of honor system parking. Build permission models into your tools, not your prompts. Classify operations by risk level and enforce that classification in code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ship machine-friendly output formats.&lt;/strong&gt; JSON by default in non-interactive contexts. Stable exit codes. Deterministic output. These are &lt;a href="https://dev.to/tumf/agentic-cli-design-7-principles-for-designing-cli-as-a-protocol-for-ai-agents-2c10"&gt;documented principles for agentic CLI design&lt;/a&gt;, and they matter because your next power user might not have hands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.apideck.com/blog/mcp-vs-api" rel="noopener noreferrer"&gt;MCP vs API&lt;/a&gt; - How MCP and REST APIs relate (Apideck blog)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.apideck.com/blog/api-design-principles-agentic-era" rel="noopener noreferrer"&gt;API Design Principles for the Agentic Era&lt;/a&gt; - Designing APIs with AI agents as first-class consumers&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.apideck.com/blog/understanding-the-security-landscape-of-mcp" rel="noopener noreferrer"&gt;Understanding the Security Landscape of MCP&lt;/a&gt; - MCP security considerations in depth&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.mmntm.net/articles/mcp-context-tax" rel="noopener noreferrer"&gt;The MCP Context Tax&lt;/a&gt; - Detailed analysis of MCP token overhead&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/tumf/agentic-cli-design-7-principles-for-designing-cli-as-a-protocol-for-ai-agents-2c10"&gt;Agentic CLI Design: 7 Principles&lt;/a&gt; - Design principles for CLIs as agent interfaces&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.scalekit.com/blog/mcp-vs-cli-use" rel="noopener noreferrer"&gt;MCP vs CLI Benchmark&lt;/a&gt; - Scalekit's head-to-head benchmark data (75 runs, Claude Sonnet 4)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.portofcontext.com/blog/cli-vs-mcp-vs-code-mode" rel="noopener noreferrer"&gt;CLI vs MCP vs Code Mode Benchmark&lt;/a&gt; - Sideko's 12-task Stripe benchmark comparing all three approaches&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.cloudflare.com/code-mode/" rel="noopener noreferrer"&gt;Code Mode: the better way to use MCP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.atcyrus.com/stories/mcp-tool-search-claude-code-context-pollution-guide" rel="noopener noreferrer"&gt;What is MCP Tool Search?&lt;/a&gt; - The Claude Code feature that addresses context pollution&lt;/li&gt;
&lt;li&gt;&lt;a href="https://x.com/yenkel/status/2032098351567487037" rel="noopener noreferrer"&gt;If you kill MCP, you don't give a s**t about security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://portofcontext.com/blog/cli-vs-mcp-vs-code-mode" rel="noopener noreferrer"&gt;CLI vs. MCP vs. Code Mode&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>cli</category>
      <category>api</category>
    </item>
    <item>
      <title>Redefining our SDKs Developer Experience</title>
      <dc:creator>samz</dc:creator>
      <pubDate>Tue, 18 Feb 2025 15:56:58 +0000</pubDate>
      <link>https://dev.to/apideck/redefining-our-sdks-developer-experience-5f7e</link>
      <guid>https://dev.to/apideck/redefining-our-sdks-developer-experience-5f7e</guid>
      <description>&lt;p&gt;We introduce our newly revamped SDKs, now powered by Speakeasy, which represent a major upgrade over our previous versions built with an open-source OpenAPI generator. These new SDKs overcome past limitations by offering significant improvements in usability, error handling, and performance. &lt;/p&gt;

&lt;p&gt;At Apideck, we are passionate about delivering the best tools to help developers build integrations effortlessly. Today, we’re thrilled to announce the release of our new generation of Apideck Unify SDKs, now powered by &lt;a href="https://www.speakeasy.com/" rel="noopener noreferrer"&gt;Speakeasy&lt;/a&gt;. These SDKs mark a significant leap forward in &lt;a href="https://pragmaticapi.substack.com/p/how-to-build-an-awesome-developer" rel="noopener noreferrer"&gt;developer experience&lt;/a&gt;, reliability, and ease of use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why SDKs Matter in the API Ecosystem
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.apideck.com/blog/what-is-an-sdk" rel="noopener noreferrer"&gt;SDKs (Software Development Kits)&lt;/a&gt; are more than just tools; they are a gateway to seamless integrations. While APIs expose the core functionalities, SDKs simplify the implementation by providing pre-built methods, abstractions, and tools that accelerate development. They bridge the gap between raw API endpoints and developer-friendly code, saving time and reducing errors.&lt;/p&gt;

&lt;p&gt;With Unified APIs like ours, which integrate services such as &lt;strong&gt;Accounting&lt;/strong&gt; (e.g., QuickBooks, Xero), &lt;strong&gt;HRIS&lt;/strong&gt; (e.g., Workday, Personio), and &lt;strong&gt;CRM&lt;/strong&gt; (e.g., Salesforce, HubSpot), the need for robust and intuitive SDKs becomes even more critical. Our goal is to help our customers focus on building their products, not wrestling with integration complexities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenges with Previous SDKs
&lt;/h3&gt;

&lt;p&gt;Until now, we relied on an open-source &lt;a href="https://github.com/OpenAPITools/openapi-generator" rel="noopener noreferrer"&gt;OpenAPI generator&lt;/a&gt; to build our SDKs. While it served us well, the maintenance and scalability challenges became apparent as we expanded our platform. Key pain points included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High Maintenance Overhead&lt;/strong&gt;: Over 4k unresolved issues in the open-source generator created friction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-idiomatic Code&lt;/strong&gt;: Generated code often lacked the language-specific nuances that developers expect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Concerns&lt;/strong&gt;: Open-source dependencies added uncertainties around updates and vulnerabilities.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why Speakeasy?
&lt;/h3&gt;

&lt;p&gt;After evaluating multiple SDK-as-a-service vendors, including &lt;a href="https://www.speakeasy.com/" rel="noopener noreferrer"&gt;Speakeasy&lt;/a&gt;, &lt;a href="https://buildwithfern.com/" rel="noopener noreferrer"&gt;Fern&lt;/a&gt; and &lt;a href="https://liblab.com/" rel="noopener noreferrer"&gt;Liblab&lt;/a&gt;, we selected &lt;a href="https://www.speakeasy.com/" rel="noopener noreferrer"&gt;Speakeasy&lt;/a&gt; as our strategic partner. Speakeasy’s philosophy aligns with our mission to deliver an outstanding developer experience. Here’s why we’re excited about this partnership:&lt;/p&gt;

&lt;p&gt;Speakeasy provides: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type Safety&lt;/strong&gt;: Fully typed SDKs ensure errors are caught early, boosting developer confidence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human Readability&lt;/strong&gt;: Intuitive, language-idiomatic code makes SDKs easier to debug and adopt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batteries Included&lt;/strong&gt;: Features like telemetry, retries, and pagination are built-in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fault Tolerance&lt;/strong&gt;: Speakeasy’s generator validates OpenAPI specs, ensuring the SDKs are robust.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimal Dependencies&lt;/strong&gt;: Lightweight SDKs reduce overhead and improve performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key Features of the New Apideck Unify SDKs
&lt;/h3&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Improved Method Naming&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We’ve reimagined method names to make them more intuitive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;crm&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;apideck&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;crm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contacts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;crm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contacts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;contact&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* contact data */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;crm&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;apideck&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;crm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contactsAll&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;crm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contactsAdd&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;contact&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* contact data */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. &lt;strong&gt;Enhanced HTTP Debugging&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Every response now includes a &lt;code&gt;httpMeta&lt;/code&gt; object, providing raw HTTP request and response details. This transparency simplifies troubleshooting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;apideck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;crm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contacts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;httpMeta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;httpMeta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. &lt;strong&gt;Streamlined Pagination&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Async iterables make handling paginated responses seamless:&lt;/p&gt;

&lt;p&gt;To use pagination in the new Typescript SDK for example, you make your SDK calls as usual, but the returned response object will also be an async iterable that can be consumed using the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of" rel="noopener noreferrer"&gt;&lt;code&gt;for await...of&lt;/code&gt;&lt;/a&gt;syntax.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Apideck&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@apideck/unify&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apideck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Apideck&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;APIDECK_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;consumerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;YOUR_CONSUMER_ID&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;YOUR_APP_ID&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;apideck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accounting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;taxRates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;serviceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;salesforce&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;assets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;equity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;expenses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;liabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;revenue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;passThrough&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;search&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;San Francisco&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id,updated_at&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Handle the page&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. &lt;strong&gt;Sophisticated Error Handling&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Language-specific exceptions ensure precise error management:&lt;/p&gt;

&lt;p&gt;For example, in our TypeScript SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Apideck&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@apideck/unify&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;BadRequestResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;NotFoundResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;PaymentRequiredResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;SDKValidationError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;UnauthorizedResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;UnprocessableResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@apideck/unify/models/errors&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apideck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Apideck&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;APIDECK_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;consumerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;YOUR_CONSUMER_ID&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;YOUR_APP_ID&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;apideck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accounting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;taxRates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;serviceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;salesforce&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;assets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;equity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;expenses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;liabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;revenue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;passThrough&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;search&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;San Francisco&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id,updated_at&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Handle the page&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// The server response does not match the expected SDK schema&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;SDKValidationError&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Pretty-print will provide a human-readable multi-line error message&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pretty&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="c1"&gt;// Raw value may also be inspected&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rawValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;BadRequestResponse&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Handle err.data$: BadRequestResponseData&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;UnauthorizedResponse&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Handle err.data$: UnauthorizedResponseData&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;PaymentRequiredResponse&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Handle err.data$: PaymentRequiredResponseData&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;NotFoundResponse&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Handle err.data$: NotFoundResponseData&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;UnprocessableResponse&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Handle err.data$: UnprocessableResponseData&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Other errors such as network errors, see HTTPClientErrors for more details&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. &lt;strong&gt;Retry Support&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Requests automatically retry on network failures, with customizable configuration.&lt;/p&gt;

&lt;p&gt;For example, in our TypeScript SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Apideck&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@apideck/unify&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apideck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Apideck&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;APIDECK_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;consumerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;YOUR_CONSUMER_ID&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;YOUR_APP_ID&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;apideck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accounting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;taxRates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;backoff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;backoff&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;initialInterval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;maxInterval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;exponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;maxElapsedTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;retryConnectionErrors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Handle the page&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. &lt;strong&gt;Custom API Clients&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Developers can now use their preferred API clients, integrating seamlessly with hooks for customization:&lt;/p&gt;

&lt;p&gt;For example, in our TypeScript SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Apideck&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@apideck/unify&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HTTPClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@apideck/unify/lib/http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;httpClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HTTPClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;// fetcher takes a function that has the same signature as native `fetch`.&lt;/span&gt;
  &lt;span class="na"&gt;fetcher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;httpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addHook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;beforeRequest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;AbortSignal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;nextRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-custom-header&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;custom value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;nextRequest&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;httpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addHook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;requestError&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Request Error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Reason:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Endpoint:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groupEnd&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sdk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Apideck&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;httpClient&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7. &lt;strong&gt;Self-Documenting SDKs&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Language-specific examples and in-SDK documentation significantly streamline the onboarding process for developers by providing clear, tailored guidance in their preferred programming language. These examples eliminate the need to translate generic code snippets, allowing developers to focus on building their applications rather than deciphering SDK usage. By showcasing idiomatic, best-practice implementations, they instill confidence in developers, ensuring a smoother and faster path to achieving their first successful integration.&lt;/p&gt;

&lt;p&gt;Moreover, in our &lt;a href="https://developers.apideck.com/apis/accounting/reference#tag/Bills/operation/billsAll" rel="noopener noreferrer"&gt;developer portal&lt;/a&gt;, every operation now includes your favorite code example, making it even easier to understand and implement unify APIs and speed up your integration.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/2GXEUu8JgAZUOSEqio4IAR/5fc4121f2189cc7441a5d12ccd0f7b85/Screenshot_2025-01-16_at_15.04.11.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/2GXEUu8JgAZUOSEqio4IAR/5fc4121f2189cc7441a5d12ccd0f7b85/Screenshot_2025-01-16_at_15.04.11.png" alt="code-samples"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Transition Plan
&lt;/h3&gt;

&lt;p&gt;We are excited to announce the next generation of our SDKs and invite all customers to transition to the new SDKs by &lt;strong&gt;May 31, 2025&lt;/strong&gt;. After this date, legacy SDKs will be officially archived and will no longer receive updates or support.&lt;/p&gt;

&lt;p&gt;To ensure a seamless migration process, all current SDKs have been marked as deprecated. We encourage you to begin migrating to the new SDKs today by following our comprehensive &lt;a href="https://developers.apideck.com/guides/sdk-migration" rel="noopener noreferrer"&gt;Migration Guide&lt;/a&gt;. This guide provides step-by-step instructions to help you transition smoothly and unlock the benefits of our updated SDKs.&lt;/p&gt;

&lt;p&gt;For more detailed guidance, you can refer to the respective SDK documentation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NodeJS SDK&lt;/strong&gt;: &lt;a href="https://github.com/apideck-libraries/sdk-typescript?tab=readme-ov-file#apideck" rel="noopener noreferrer"&gt;View Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python SDK&lt;/strong&gt;: &lt;a href="https://github.com/apideck-libraries/sdk-python?tab=readme-ov-file#apideck" rel="noopener noreferrer"&gt;View Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PHP SDK&lt;/strong&gt;: &lt;a href="https://github.com/apideck-libraries/sdk-php?tab=readme-ov-file#apideck" rel="noopener noreferrer"&gt;View Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;.Net SDK&lt;/strong&gt;: &lt;a href="https://github.com/apideck-libraries/sdk-csharp?tab=readme-ov-file#apideck" rel="noopener noreferrer"&gt;View Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have any questions or need further assistance, feel free to contact our Support team at &lt;strong&gt;support[at]apideck.com&lt;/strong&gt;. We’re here to help ensure a smooth migration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Looking Ahead
&lt;/h3&gt;

&lt;p&gt;The new Apideck Unify SDKs represent our commitment to removing friction for developers and delivering a superior integration experience. We’re confident these changes will empower our customers to achieve their goals faster and more efficiently.&lt;/p&gt;

&lt;p&gt;Ready to elevate your integration experience? Start exploring the new &lt;a href="https://developers.apideck.com/sdks" rel="noopener noreferrer"&gt;Apideck Unify SDKs today&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>api</category>
      <category>ai</category>
      <category>dx</category>
      <category>sdk</category>
    </item>
  </channel>
</rss>
