Key takeaways

  • With a 40 ms client default configured, pollInterval: 0 took precedence and produced 86 and 88 tasks/get calls over roughly 113–117 ms, with a 1.3 ms median observed gap.
  • The 5 ms server hint produced 21 and 22 calls with a 6.3 ms median gap; omitting the hint let the 40 ms client default produce four calls with a 42.1 ms median gap.
  • When the server selected 300 ms and AbortSignal fired at 40 ms, the automatic stream settled at 302.0 and 301.6 ms—about 262 ms after abort—because the signal check followed the timer.
  • The SDK validator and polling loop supplied no interval floor, ceiling or host clamp in the inspected path. The protocol calls the value a suggestion, so production hosts should own an allowlisted polling envelope.
  • Backpressure and cancellation latency are AI-agent control-plane decisions. Do not let a remote MCP endpoint silently choose either one for a fleet of durable tool tasks.
01

The sharp question: who controls an agent’s polling loop?

A durable MCP tool task may run for minutes while an agent host asks tasks/get for status. The server knows how often useful state can change. The host knows its concurrency, request budget, shutdown target and trust boundary. A pollInterval field sits directly between those two owners.

In the SDK 1.30.0 automatic task stream, the server won. A zero-millisecond value displaced the client’s configured 40 ms default and created a near-tight loop. At the other extreme, a 300 ms value also delayed local abort because the stream checked its signal only after sleeping.

02

Chronology: durable tasks made polling part of the protocol

The task proposal entered the TypeScript SDK through the SEP-1686 implementation merged November 27, 2025. It added durable task IDs, status retrieval and result recovery for work that may outlive one request. The 2025-11-25 protocol version tells requestors to poll tasks/get and lets receivers suggest a pollInterval.

On December 10, an independent implementer opened protocol issue 1955 after seeing agents over-poll, burn tokens and fill context while waiting. A different server-side AbortSignal polling issue was filed May 5, 2026. SDK 1.30.0 was published July 27. This AccessAllGPT client-side timing reproduction ran September 3.

03

The specification provides a hint, not a safe operating envelope

The normative task-retention text says receivers may include pollInterval in tasks/get and requestors should respect it. The field is described in milliseconds. The reviewed version does not state a positive minimum, finite maximum, jitter rule, concurrency policy or maximum time before a cancelled caller must wake.

“Should respect” is not “must execute unchanged.” A host can honor server backpressure while applying its own safety policy. That policy is especially important when one host connects to third-party MCP servers or fans out many long-running AI tool calls.

04

The distributed schema accepted a plain optional number

The pinned specification interface calls pollInterval an optional number. In the installed SDK 1.30.0 artifact, the task schema used z.number().optional(). The inspected validator did not add integer, positive, minimum or maximum constraints.

This does not prove that a server returning zero violates the protocol; the reviewed text does not publish a lower bound. It does establish that schema validation did not protect the client from values that can produce pathological scheduling behavior.

05

The SDK gives the server value first refusal

The automatic loop computes task.pollInterval ?? clientDefault ?? 1000. Nullish coalescing means zero is a present value, so it wins over defaultTaskPollInterval. The loop then passes that value directly to callback setTimeout.

There is no Math.min, Math.max or equivalent clamp in the inspected requestStream slice. A configured client default is therefore a fallback for omission, not a floor, ceiling or policy override.

06

The fixture used one task and four controlled timing paths

The in-process transport exposed one synthetic tool named run_synthetic_inference. It returned task synthetic-inference-1 and changed the status from working to completed according to a monotonic local clock. There was no model invocation; “inference” names the production-shaped control-plane scenario only.

Three completion paths used a nominal 125 ms task: server interval zero, server interval 5 ms, and omitted interval with client default 40 ms. A separate non-completing task returned 300 ms while the caller scheduled AbortSignal for 40 ms. Per-request timeout was 1,000 ms so it did not confound the timer result.

07

Zero milliseconds produced 86 and 88 status requests

The zero-hint scenario completed successfully in both clean runs. It sent 86 tasks/get requests over 113.3 ms in run one and 88 over 116.8 ms in run two. The median observed gap was 1.3 ms in both runs.

Those elapsed times begin at the tool-stream call, while the synthetic completion clock begins when its transport is constructed, so they are slightly below the nominal 125 ms task duration. Counts and gaps are fixture observations, not network throughput or an SDK benchmark.

08

Five milliseconds still multiplied the request load

With pollInterval 5, the same synthetic task generated 21 and 22 tasks/get calls. Both runs measured a 6.3 ms median gap and completed in 124.6 and 129.3 ms. Timer overhead explains why observed gaps need not equal the requested delay exactly.

The point is the controlled ratio, not nanosecond precision: the zero path produced more than twice the calls of the 5 ms path in both runs. At fleet scale, multiplying that loop by tasks, tenants and reconnects can turn a small field into meaningful control-plane pressure.

09

Omission was the only path that used the client default

When the server omitted pollInterval, defaultTaskPollInterval 40 finally applied. The harness sent four tasks/get calls in each run, with 42.1 ms median gaps and 126.3 ms total stream time.

That control rules out a broken client option. The option worked as implemented, but it could not bound a present server value. Operators should not read “default” as “minimum safe polling interval.”

10

The slow side of the same field delayed abort

The fourth scenario returned 300 ms, then aborted the agent budget at 40 ms. Only the first tasks/get occurred. The stream eventually yielded its expected error containing “agent budget ended,” but not until 302.0 ms in run one and 301.6 ms in run two.

Measured from the scheduled abort, settlement lagged by 262.0 and 261.6 ms. This was not a blocked network request: the transport answered immediately. The remaining delay came from the polling sleep already in progress.

11

The signal check sits after a non-abortable timer

The pinned requestStream awaits new Promise(resolve => setTimeout(resolve, pollInterval)), then calls signal.throwIfAborted(). The callback timer has no signal and is not cleared by that path. Once sleeping, the generator learns about abort only when the selected interval expires.

Node documents an AbortSignal option for its timers-promises API, but the SDK path inspected here uses callback setTimeout wrapped in a Promise. This is implementation evidence for 1.30.0, not a claim that all JavaScript sleeps or all MCP SDKs behave this way.

12

Low and high values create different agent failures

A very low interval increases status traffic, logging, parsing and task-store reads. It can magnify a faulty or malicious server hint across every active agent task. A very high interval reduces that load but delays completion visibility and, in this path, delays local shutdown or budget enforcement.

The right interval is workload-specific. The invariant is ownership: remote advice can influence scheduling, but the host must retain enforceable bounds for request rate, deadline responsiveness and fair sharing across tenants.

13

Independent reports identify adjacent polling pressure

Protocol issue 1955 reports an agent-level failure mode: without a wait operation, agents may fixate on polling and consume tokens or context. This fixture did not involve model turns, so it neither reproduces nor quantifies that report. It shows a lower-level automatic loop where excessive protocol calls can arise without model choice.

SDK issue 2018 concerns server-side automatic polling that allegedly persisted after request cancellation. That is not the client requestStream tested here. Both records are useful because they show polling and cancellation pressure on opposite sides of the connection; neither establishes prevalence.

14

Clamp suggestions before they become schedules

A production host should parse pollInterval as finite and integer, reject or replace negative and non-finite values, and clamp accepted suggestions between locally configured floor and ceiling. Add randomized jitter where synchronized clients could stampede one server. Keep the server identity and original value in telemetry.

Choose the floor from server capacity, task-store cost and fleet concurrency. Choose the ceiling from user-visible freshness and shutdown requirements. For example, effectiveMs = min(max(serverMs ?? defaultMs, floorMs), ceilingMs) is a policy shape, not a universal set of numbers.

15

Make the wait itself interruptible

A host-owned poller should race sleep against AbortSignal or use an abort-aware timer, then reconcile the durable task according to cancellation policy. Waking the local loop does not prove the remote task stopped; the separate cancellation investigation showed that durable tasks require an explicit tasks/cancel decision.

Preserve taskId before waiting. On local abort, return control within a measured grace target, decide whether to detach or request task cancellation, and record the final known state. Do not solve delayed local abort by discarding the handle needed for recovery.

16

Put a budget around the whole task fleet

Per-task clamping is necessary but incomplete. Limit concurrent pollers per server and tenant, cap aggregate tasks/get rate, use exponential backoff when status is unchanged where compatible with the contract, and apply circuit breakers for errors or rate limits. Prefer notifications or a defined wait operation when both sides support a tested alternative.

Measure status requests per active task, observed gaps, server hints, effective intervals, unchanged-poll streaks, abort-to-settlement latency, task-store latency and terminal-result delay. Alert on zeros, outliers, synchronized bursts and defaults that are never taking effect.

17

What this run does not establish

The experiment did not use a language model, production host, remote server, real inference job, task database, HTTP or stdio transport, proxy, authentication, concurrency, rate limit, network latency, notification path, wait extension, v2 package, another SDK, browser, credential, user data or external side effect. One process and one synthetic task were used per scenario.

It does not establish a vulnerability, denial of service, real-world prevalence, CPU percentage, token consumption, server cost, ideal interval or behavior on another runtime. Callback timer timing is scheduler-dependent. MCP tasks are experimental, and the issue reports are attributed implementation records rather than independent reproduction of these exact measurements.

18

Decision: deploy task polling only behind a host-owned envelope

Deploy automatic MCP task polling only when every server hint is validated and clamped, waits are interruptible, aggregate request rate is bounded, task identity survives abort, and the exact SDK, runtime and transport pass low, normal, high, malformed, cancellation and concurrency cases. Treat a client default as insufficient unless tests prove it acts as policy.

Circuit-break when a server can force a near-tight loop, abort-to-settlement exceeds the host objective, poll traffic is unbounded across tasks, or operators cannot reconcile durable state after a local stop. The server may suggest when status is useful. The AI agent host must decide how much polling and shutdown latency its production system can afford.

19

Copy-ready MCP task polling gate

Complete this record before an AI agent host automatically polls durable MCP tasks.

Entries stay in this browser tab and are not submitted to AccessAllGPT. Blank responses are copied as [Unresolved].

Server, protocol, SDK, runtime, transport, tool, task store, expected duration and consequence class.

Accepted type and units; finite/integer checks; handling for zero, negative, fraction, omission and oversized values.

Default, local floor, local ceiling, jitter, unchanged-status backoff and authority to override server hints.

Concurrent tasks, requests per second by server and tenant, fairness, queueing, rate-limit response and circuit breaker.

Overall agent budget, interruptible wait mechanism, abort-to-settlement target and measured worst case.

Preserved task ID, detach versus tasks/cancel policy, terminal reconciliation, result retrieval and replay protection.

Notifications, wait/join extension or transport stream support; negotiation, fallback and interoperability evidence.

Original hint, effective interval, observed gap, tasks/get count, unchanged streak, errors, abort and terminal latency.

Zero, low, omitted, normal, high, malformed and changing hints; abort during sleep; many tasks; slow store; reconnect.

Near-tight loop, fleet budget breach, delayed abort, lost task identity, unreconciled state or changed SDK behavior.

Primary sources

  1. Tasks (protocol version 2025-11-25)Model Context Protocol Specification · Reviewed: Task lifecycle, task retention, polling guidance, task fields, result retrieval, cancellation and security considerations · Retrieved · Supports: The experimental task specification lets receivers include a millisecond pollInterval in tasks/get and says requestors should respect it; the published guidance states no minimum, maximum or abort-latency rule.
  2. MCP schema at the 2025-11-25 specification commitModel Context Protocol · Reviewed: Task interface, status, timestamps, TTL, optional pollInterval and task request and result shapes · Retrieved · Supports: The pinned protocol schema describes pollInterval only as an optional number of milliseconds and does not express a lower bound, upper bound or client policy field.
  3. TypeScript SDK release 1.30.0Model Context Protocol TypeScript SDK · Reviewed: Release identity, publication date, v1.x target, package changes and full-changelog reference · Retrieved · Supports: The official release record identifies SDK 1.30.0 as the v1.x package release published July 27, 2026 and anchors the tested implementation version.
  4. Protocol task stream at SDK 1.30.0Model Context Protocol TypeScript SDK · Reviewed: requestStream task creation, tasks/get loop, terminal handling, pollInterval selection, timer wait, signal check and request timeout path · Retrieved · Supports: The pinned implementation chooses task.pollInterval before the configured client default, passes it directly to setTimeout, and checks AbortSignal only after that timer resolves.
  5. Task schemas at SDK 1.30.0Model Context Protocol TypeScript SDK · Reviewed: Task creation parameters, task metadata, task object schema, pollInterval validator and generated protocol types · Retrieved · Supports: The distributed SDK task validator represented pollInterval as an optional Zod number without integer, positive, minimum or maximum constraints in the artifact tested.
  6. @modelcontextprotocol/sdk 1.30.0 registry recordnpm Registry · Reviewed: Package name, version, repository, publication metadata, distribution integrity and runtime requirement · Retrieved · Supports: The registry record binds the installable SDK package used by both clean reproductions to version 1.30.0 and its published distribution artifact.
  7. Tasks SEP-1686 missing wait/await to prevent excessive pollingModel Context Protocol issue tracker · Reviewed: Independent implementation report, excessive-polling concern, token and context impact, proposed wait operation, discussion and closure metadata · Retrieved · Supports: An independent implementer reported in December 2025 that agents could fixate on task polling and consume context, motivating a wait or join operation; it is experience evidence, not a reproduction of this SDK timer path.
  8. handleAutomaticTaskPolling ignores AbortSignal; cancelled requests poll indefinitelyModel Context Protocol TypeScript SDK issue tracker · Reviewed: Independent server-side report, polling loop, cancellation handling, multi-tenant impact, comparison implementation and proposed fix · Retrieved · Supports: A separate May 2026 report describes server-side automatic polling that did not stop on request cancellation, corroborating lifecycle pressure around polling while concerning a different code path from this client fixture.
  9. Timers: scheduling timersNode.js Documentation · Reviewed: setTimeout delay coercion and range behavior, scheduling semantics, AbortSignal-capable timers promises and cancellation · Retrieved · Supports: Node documents callback setTimeout scheduling and a separate timers-promises API that can accept AbortSignal, relevant to interpreting the SDK timer boundary without claiming exact scheduler performance.

Limitations

This is a bounded local reproduction on Node v24.10.0, Darwin arm64, @modelcontextprotocol/sdk 1.30.0 and Zod 3.25.76 using a synthetic in-process transport. Three nominal 125 ms tasks returned server pollInterval values of zero, 5 ms or omission with a 40 ms client default; one non-completing task returned 300 ms and received AbortSignal at 40 ms. The runs did not test a model, production agent, remote server, real inference, task database, HTTP or stdio, proxy, authentication, concurrency, rate limiting, network latency, notifications, wait extension, v2, another SDK/runtime, external side effect or user data. Counts, gaps and elapsed times are directional fixture observations from two runs, not throughput, CPU, cost or security benchmarks. The evidence does not establish prevalence, exploitability, an ideal interval or a universal defect. MCP tasks remain experimental.

Disclosures

AccessAllGPT created and ran only the synthetic local fixture in the checked-in harness. Protocol issue 1955 and TypeScript SDK issue 2018 are independent public implementation records about adjacent polling behavior; their claims are attributed and are not presented as reproductions of this client experiment. No model, production client, third-party server, credential, endpoint, inference workload, payment, external action or user data was used. The MCP project, issue reporters, SDK maintainers, Node.js project, npm and Zod maintainers did not review this article or provide access, private data, payment or endorsement. AccessAllGPT Research is operated by NeuralArc, is independent, and is not affiliated with OpenAI, Anthropic, the MCP project, Node.js, npm or Zod. Publication-wide relationships are listed on the disclosures page.

Further AccessAllGPT guidance

  1. Stopping an MCP Client Did Not Stop the AI Agent’s Task
  2. An MCP Task Timeout Is Not an AI Agent Deadline
  3. A Failed MCP Task Can Hide the Error an AI Agent Needs
  4. An Optional MCP Task Schema Can Strand an Agent’s Completed Result
  5. A Lost MCP SSE Response Can Hold an AI Agent Until Its Timeout
  6. Design an Agent Benchmark That Predicts Production
  7. AccessAllGPT Research methodology
  8. Publication disclosures

Continue the research

Get evidence-led updates for teams making production AI decisions.