Skip to content

v0.8.0 — Dataverse service protection limits and request timeouts

Choose a tag to compare

@rededis rededis released this 02 Oct 11:29
· 33 commits to main since this release
9bd51eb

Added

  • DATAVERSE_REQUEST_TIMEOUT_MS (default 30000, maximum 120000, matching Dataverse's own 2-minute limit on a message operation): every Dataverse request and the token request to Microsoft Entra ID now time out instead of hanging a tool call indefinitely. The error says which request timed out. A value that is not a whole number from 1 to 120000 is reported through dataverse_setup like a missing variable, rather than silently replaced by the default (#73).

  • Handling of Dataverse service protection limits (closes #74). A 429 Too Many Requests used to fail the tool call with the raw Dataverse error. Now:

    • A throttled request is resent after the wait its Retry-After header asks for, up to DATAVERSE_MAX_ATTEMPTS sends in total (default 3). Without a usable header the server backs off 2 s, then 4 s, and so on. While it waits, no other request is sent, because Dataverse extends the wait for a client that keeps sending.
    • If the waits of one request would add up to more than DATAVERSE_MAX_RETRY_WAIT_MS (default 15000), or the attempts run out, the tool call fails with "Dataverse is busy: a service protection limit was reached. Retry in N s." The raw Dataverse 429 text is no longer shown.
    • At most DATAVERSE_MAX_CONCURRENCY requests are in flight (default 8). Up to DATAVERSE_MAX_QUEUE_LENGTH more (default 100) wait their turn in arrival order for at most DATAVERSE_MAX_QUEUE_WAIT_MS (default 10000); beyond either bound the call fails with a "Dataverse is busy … Retry later" error instead of queuing indefinitely.
    • All five variables are optional. An out-of-range value is reported through dataverse_setup rather than replaced by the default.

    Behaviour to know about: writes are retried after a 429 like reads, as Microsoft's own clients do. A 429 holds other requests back for the wait Dataverse asked for, but no longer than DATAVERSE_MAX_RETRY_WAIT_MS (closes #88): in a live test Dataverse answered light requests straight after a 429 with Retry-After: 300, so a longer pause would only have been downtime of our own making. With the defaults the limits add at most 25 s of waiting to a request, chosen so that one request at the default timeout still fits the 60 s a client built on the MCP TypeScript SDK waits by default; the README gives the full arithmetic.

Changed

  • Tools are registered by purpose instead of by topic (closes #72): metadata-read, data-read, data-write, actions, functions and development, as named in ADR-0001 §4. Nothing a client sees changes except order: tool names, descriptions, input schemas and DATAVERSE_ALLOW_DELETE behaviour are identical, pinned by a tools/list snapshot taken before the change. tools/list now returns tools grouped this way rather than in the old per-file order.
  • Tool input schemas are built once at module load instead of on every registration, ahead of SDK v2 building a server instance per HTTP request.
  • When the cached token has expired, concurrent tool calls share one token request instead of each sending their own (#73).
  • The client reports failures as typed errors (DataverseApiError with status and the Dataverse error code, DataverseTimeoutError, DataverseNetworkError, DataverseAuthError), and tools map "not found" by status rather than by matching message text. API error messages read as before. A network failure now says which request failed and why, instead of a bare fetch failed (#73).
  • HTTP execution sits behind a RequestExecutor interface, the seam for throttling handling (#74), acting on behalf of a user (#77) and audit logging (#78) (#73).

Fixed

  • The server no longer writes a non-protocol line to stdout at startup. When a .env file was loaded, dotenv 17 printed a banner (◇ injected env (7) from .env // tip: …) with console.log, and on stdio stdout is the MCP protocol stream. Clients built on the MCP TypeScript SDK reported a parse error for that line and carried on; a stricter client could refuse the connection. Present in every release since 0.1.0.
  • get_picklist_options, add_attribute and create_entity no longer report "Global OptionSet not found" for a server error whose body happens to contain "404". They matched 404 anywhere in the message; they now check the status (#73).