zeroclaw-labs/zeroclaw · warning · anyhow::Error

MCP server `{}` does not support resources

Error message

MCP server `{}` does not support resources

What it means

list_resources is capability-gated: the server's initialize response did not advertise the resources capability, so the client refuses to send resources/list rather than let the server error downstream. The gate reads the capabilities parsed during handshake, so it reflects exactly what this server build declared.

Source

Thrown at crates/zeroclaw-tools/src/mcp_client.rs:795

        let resp = self
            .dispatch_rpc(rpc_method, params, tool_timeout, &operation)
            .await?;

        if let Some(err) = resp.error {
            bail!("MCP `{rpc_method}` error {}: {}", err.code, err.message);
        }
        let result = resp.result.unwrap_or(serde_json::Value::Null);
        let server_name = self.inner.lock().await.config.name.clone();
        check_result_is_error(&result, rpc_method, &server_name)?;
        Ok(result)
    }

    /// `resources/list` — capability-gated.
    pub async fn list_resources(&self, cursor: Option<String>) -> Result<McpResourcesListResult> {
        {
            let inner = self.inner.lock().await;
            if !inner.capabilities.supports_resources() {
                bail!(
                    "MCP server `{}` does not support resources",
                    inner.config.name
                );
            }
        }
        let params = match cursor {
            Some(c) => json!({ "cursor": c }),
            None => json!({}),
        };
        let raw = self.dispatch_method("resources/list", params).await?;
        serde_json::from_value(raw).context("failed to parse resources/list result")
    }

    /// `resources/read` — capability-gated.
    pub async fn read_resource(&self, uri: &str) -> Result<McpResourceContents> {
        {
            let inner = self.inner.lock().await;
            if !inner.capabilities.supports_resources() {

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Switch to an MCP server that advertises resources (e.g. a filesystem server) if you need them
  2. Check the server's initialize response or client debug logs to confirm which capabilities it actually declared
  3. Guard resource calls so unsupported servers are skipped instead of erroring the whole flow
  4. If the server should support resources, change to a build that declares them

Example fix

// before
let res = client.list_resources(None).await?;

// after: skip servers without the capability
match client.list_resources(None).await {
    Ok(res) => handle(res),
    Err(e) if is_unsupported_resources(&e) => skip_server(&name),
    Err(e) => return Err(e),
}
Defensive patterns

Strategy: validation

Validate before calling

// Probe once per server and cache the result instead of failing per call
let supports = match client.list_resources(None).await {
    Ok(_) => true,
    Err(e) if is_unsupported_resources(&e) => false,
    Err(e) => return Err(e),
};
if supports {
    walk_resources(&client).await?;
}

Type guard

fn is_unsupported_resources(err: &anyhow::Error) -> bool {
    err.to_string().contains("does not support resources")
}

Try / catch

Catch and treat as a capability signal, not a fault: skip resource discovery for this server and continue; any other error propagates.

Prevention

When it happens

Trigger: Configuring a tools-only or prompts-only MCP server (no resources in its initialize result) and calling list_resources; a proxy that strips capability fields from the initialize result; older server builds predating the resources part of the protocol.

Common situations: Pointing generic resource-walking code at specialized tool servers; version drift where the deployed server build dropped resource support.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/123bc62bbeee2dcb. Report an issue: GitHub.