<?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: Zebu Data</title>
    <description>The latest articles on DEV Community by Zebu Data (@zebudata).</description>
    <link>https://dev.to/zebudata</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4157425%2F95e1b06e-6f59-4f46-8ecd-1c938681e79d.jpg</url>
      <title>DEV Community: Zebu Data</title>
      <link>https://dev.to/zebudata</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zebudata"/>
    <language>en</language>
    <item>
      <title>What we learned publishing 7 MCP servers to the official registry</title>
      <dc:creator>Zebu Data</dc:creator>
      <pubDate>Sat, 10 Oct 2026 15:19:05 +0000</pubDate>
      <link>https://dev.to/zebudata/what-we-learned-publishing-7-mcp-servers-to-the-official-registry-1f7i</link>
      <guid>https://dev.to/zebudata/what-we-learned-publishing-7-mcp-servers-to-the-official-registry-1f7i</guid>
      <description>&lt;p&gt;The official MCP Registry is where MCP clients and directories look up servers, so if you build one, you want it listed there. We build web scrapers on Apify and wrap groups of them as remote MCP servers, and over the last two weeks we published 7 of them: 4 on Oct 1, then 3 more on Oct 7, when we also updated 3 of the first 4. Here's what we ran into, with the real limits and errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Decide what one server should hold
&lt;/h2&gt;

&lt;p&gt;Our first instinct was one big server with every tool. We split them by use case instead: local business leads, customer reviews, YouTube data and so on. Each one holds between 2 and 7 tools.&lt;/p&gt;

&lt;p&gt;Two reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Models pick the wrong tool less often&lt;/strong&gt; when they only see a handful of similar ones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your gateway may add tools of its own.&lt;/strong&gt; Ours adds 4 helper tools to every server, for checking a job, paging through results, reading a file and stopping a job. A 7-tool server shows up as 11 tools in &lt;code&gt;tools/list&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's a side benefit: each server gets its own listing, so someone searching a directory for "reviews" or "YouTube" finds the one that matches.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. server.json has hard limits
&lt;/h2&gt;

&lt;p&gt;A minimal remote entry looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"io.github.your-user/your-server"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your Server"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"One sentence about what the tools do."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"websiteUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"remotes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"streamable-http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mcp.example.com/mcp"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The 2025-12-11 schema caps &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; at 100 characters each, and &lt;code&gt;name&lt;/code&gt; must look like &lt;code&gt;namespace/server&lt;/code&gt;: letters, digits, dots and dashes, plus underscores after the slash. Our first description was 106 characters. We caught it with a length check in our build script, but &lt;code&gt;mcp-publisher validate server.json&lt;/code&gt; catches it too, and it checks against the live registry.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Your namespace comes from your GitHub login
&lt;/h2&gt;

&lt;p&gt;For &lt;code&gt;io.github.&amp;lt;user&amp;gt;/...&lt;/code&gt; names, the publisher authenticates you through GitHub with a device code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mcp-publisher login github      &lt;span class="c"&gt;# shows a code to enter at github.com/login/device&lt;/span&gt;
mcp-publisher validate server.json
mcp-publisher publish server.json
mcp-publisher &lt;span class="nb"&gt;logout&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Approve the code while signed in as the account that owns the namespace. One login covers as many publishes as you need. If you're on a shared machine, log out when you're done so the token doesn't stay on disk. We also checked the binary's SHA-256 against the release's checksums file before running it, since it handles a GitHub token.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. An update is a new version, and the old one stays
&lt;/h2&gt;

&lt;p&gt;To change a server you publish it again with a higher &lt;code&gt;version&lt;/code&gt;. The registry keeps both: after our update, &lt;code&gt;local-business-leads&lt;/code&gt; showed 1.0.0 with &lt;code&gt;isLatest: false&lt;/code&gt; and 1.1.0 with &lt;code&gt;isLatest: true&lt;/code&gt;, both still &lt;code&gt;active&lt;/code&gt;. Clients that read the registry should take the latest, but don't assume an old version disappears.&lt;/p&gt;

&lt;p&gt;The public API can also be slow. A search that returns two entries took about 23 seconds for us, so give scripts a generous timeout.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Keep tool names under 64 characters
&lt;/h2&gt;

&lt;p&gt;Our gateway names each tool &lt;code&gt;owner--tool-name&lt;/code&gt;. One of ours came out at 66 characters, so it was cut to 64 and given a short hash suffix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...--ebay-product-reviews-scraper-with-advanced--60e9
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cut isn't arbitrary: model APIs such as OpenAI's and Anthropic's only accept tool names up to 64 characters. The model still gets the tool, but with a name that's harder to match to a user's request. If you control the underlying names, keep them short.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Directories list you automatically, and may call you unhealthy
&lt;/h2&gt;

&lt;p&gt;We never submitted anything to Glama, but it picked up all our servers from the registry within about a day, and later picked up our updated descriptions too.&lt;/p&gt;

&lt;p&gt;Every one of them is marked &lt;strong&gt;Unhealthy&lt;/strong&gt;. Glama's checker connects without credentials, and our servers require the user to sign in (OAuth), so the check can't get past the start. The fix is to claim the listing and add a test profile that Glama uses only for health checks. If you do that, give it a limited credential, not your main one.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Awesome lists have rules, and a queue
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/punkpeye/awesome-remote-mcp-servers" rel="noopener noreferrer"&gt;awesome-remote-mcp-servers&lt;/a&gt; is the main curated list for remote servers. Its CONTRIBUTING.md asks for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an endpoint that answers an MCP &lt;code&gt;initialize&lt;/code&gt; request, which CI checks on every PR,&lt;/li&gt;
&lt;li&gt;a Glama connector badge, which CI also checks,&lt;/li&gt;
&lt;li&gt;a star on the repo from the account opening the PR,&lt;/li&gt;
&lt;li&gt;one server per PR, in alphabetical order, with a description of up to 120 characters ending in a period,&lt;/li&gt;
&lt;li&gt;a marker for the auth type: none, API key or OAuth.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What surprised us was the queue. For almost a week after we opened our PR, nothing in the repo was merged at all. Then the maintainer merged almost 400 PRs in a few days, and ours wasn't one of them: other entries had landed next to ours in the same section, so it now had a merge conflict. A comment on the PR asked us to rebase or merge the latest &lt;code&gt;main&lt;/code&gt; first. If you submit, watch your PR and fix conflicts fast, because merges come in batches.&lt;/p&gt;

&lt;p&gt;One more trap: the badge check calls Glama. When Glama was briefly down, our PR failed with "no such connector". Editing the PR description re-ran the checks once it was back.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. A checklist before you publish
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;One server per use case, with a tool count your model can handle.&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;initialize&lt;/code&gt; and &lt;code&gt;tools/list&lt;/code&gt; on the real URL, with and without credentials, and read the tool names.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;title&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; within 100 characters, then &lt;code&gt;mcp-publisher validate&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Bump &lt;code&gt;version&lt;/code&gt; on every change.&lt;/li&gt;
&lt;li&gt;Log out of the publisher on shared machines.&lt;/li&gt;
&lt;li&gt;A day later, check how directories show your server, health status included.&lt;/li&gt;
&lt;li&gt;For curated lists: one PR per server, then keep an eye on it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Are you publishing MCP servers too? We'd like to know which directories your users actually find you through.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>agents</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Remote MCP tools that run long: what your agent actually gets back</title>
      <dc:creator>Zebu Data</dc:creator>
      <pubDate>Fri, 02 Oct 2026 17:22:01 +0000</pubDate>
      <link>https://dev.to/zebudata/remote-mcp-tools-that-run-long-what-your-agent-actually-gets-back-4gf6</link>
      <guid>https://dev.to/zebudata/remote-mcp-tools-that-run-long-what-your-agent-actually-gets-back-4gf6</guid>
      <description>&lt;p&gt;Most MCP demos show a tool that answers in a second. Real tools often don't: a scraper, a crawl or a report job can take minutes. We build web scrapers on Apify, so this is a problem we run into daily. We wanted to know what an agent actually receives when a remote MCP tool runs longer than one call, so we traced the raw JSON-RPC traffic against Apify's hosted MCP server (&lt;code&gt;mcp.apify.com&lt;/code&gt;, version 0.17.1 at the time) on Oct 2, 2026. The job: a YellowPages scraper reading one results page, which takes about 35 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Discovery: one tool per job, plus helpers
&lt;/h2&gt;

&lt;p&gt;After &lt;code&gt;initialize&lt;/code&gt;, &lt;code&gt;tools/list&lt;/code&gt; returned one tool per scraper we had allowed through the server URL, plus four helper tools that only make sense for long-running work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;get-actor-run&lt;/code&gt; checks a run's status and can wait for it,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get-dataset-items&lt;/code&gt; reads the results in pages,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get-key-value-store-record&lt;/code&gt; reads a stored file or record,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;abort-actor-run&lt;/code&gt; stops a run.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The helpers are the first hint: the server expects the agent to come back for results.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The call returns early
&lt;/h2&gt;

&lt;p&gt;The agent calls the scraper tool with ordinary arguments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"searchKeyword"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"plumbers"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Austin, TX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"maxPages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response arrived after about 30 seconds, while the job was still running. It had two content parts. The first is structured:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"runId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"RUNNING"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"storages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"datasets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"itemCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second is plain text written for the model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RUNNING for 30s. In progress. 23 results so far.
Use get-actor-run with runId=… and waitSecs=30 to poll for completion.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That second part is the interesting design choice. The server returns machine-readable state and also tells the model, in plain English, what to do next. An agent that only parses the JSON still works. An agent that only reads text also knows to poll.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Polling has a ceiling
&lt;/h2&gt;

&lt;p&gt;We first asked &lt;code&gt;get-actor-run&lt;/code&gt; to wait 60 seconds and then 120. Both came back as a JSON-RPC error, not a tool result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MCP error -32602: Invalid arguments for tool "get-actor-run".
Validation errors: /waitSecs: must be &amp;lt;= 45.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;waitSecs: 30&lt;/code&gt; the call returned &lt;code&gt;SUCCEEDED&lt;/code&gt;, the run time (about 35 seconds) and the dataset item count (37). The result also carried a &lt;code&gt;_meta&lt;/code&gt; block with the run's platform usage, which a client can show the user before it starts anything bigger.&lt;/p&gt;

&lt;p&gt;Two things to handle here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validation problems arrive as protocol errors (&lt;code&gt;-32602&lt;/code&gt;), not as tool results with &lt;code&gt;isError&lt;/code&gt;. If your client only checks &lt;code&gt;isError&lt;/code&gt;, it will miss them.&lt;/li&gt;
&lt;li&gt;The wait is capped, so a five-minute job means several polls. Your loop needs a total deadline, not just a per-call wait.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Reading results in pages
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;get-dataset-items&lt;/code&gt; takes the dataset ID plus &lt;code&gt;offset&lt;/code&gt; and &lt;code&gt;limit&lt;/code&gt;, and returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"datasetId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"itemCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"totalItemCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"offset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a big job, page through with &lt;code&gt;offset&lt;/code&gt; instead of pulling everything into the model's context at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. The trap: SUCCEEDED with zero items
&lt;/h2&gt;

&lt;p&gt;The same evening a second test job, a Google Maps scraper, ran for about 157 seconds and finished with status &lt;code&gt;SUCCEEDED&lt;/code&gt;, but &lt;code&gt;get-dataset-items&lt;/code&gt; came back empty:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"itemCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"totalItemCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"offset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An upstream dependency was failing, and the job ended cleanly with nothing to show. If your agent treats &lt;code&gt;SUCCEEDED&lt;/code&gt; as "done, report success", it will tell the user everything worked. Check the item count, and treat "succeeded but empty" as a result the user needs to hear about.&lt;/p&gt;

&lt;h2&gt;
  
  
  A checklist for long-running MCP tools
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Expect an early return.&lt;/strong&gt; Store the run ID the first response gives you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Poll within the server's limits&lt;/strong&gt; (45 seconds per wait here), with an overall deadline and a clear message if it runs out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check protocol errors and &lt;code&gt;isError&lt;/code&gt;.&lt;/strong&gt; They come back in different places.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read results in pages&lt;/strong&gt; to protect the context window.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treat "succeeded, zero items" as a warning&lt;/strong&gt;, not a success.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Abort what you no longer need.&lt;/strong&gt; If the user changes the question halfway, &lt;code&gt;abort-actor-run&lt;/code&gt; saves the rest of the job's cost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expose fewer tools.&lt;/strong&gt; A server-side allow list keeps the model from picking the wrong tool and limits what it can touch.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;How does your MCP client deal with tools that outlive one call: polling, progress notifications, or something else? We're curious what holds up in production.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written with AI assistance. Every request and response above comes from real calls we made on Oct 2, 2026.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>agents</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
