{"record":{"id":"1eef524d9f299095","repo":"Hmbown/CodeWhale","slug":"mcp-resource-uri-uri-was-not-advertised-by-ser","errorCode":null,"errorMessage":"MCP resource URI '{uri}' was not advertised by server '{server_name}'","messagePattern":"MCP resource URI '(.+?)' was not advertised by server '(.+?)'","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp.rs","lineNumber":2986,"sourceCode":"        }\n        prompts\n    }\n\n    /// Read a resource from a specific server\n    pub async fn read_resource(\n        &mut self,\n        server_name: &str,\n        uri: &str,\n    ) -> Result<serde_json::Value> {\n        let global_timeouts = self.config.timeouts;\n        let conn = self.get_or_connect(server_name).await?;\n        let advertised_literal = conn.resources().iter().any(|resource| resource.uri == uri);\n        let advertised_template = conn\n            .resource_templates()\n            .iter()\n            .any(|template| resource_uri_matches_template(uri, &template.uri_template));\n        if !advertised_literal && !advertised_template {\n            anyhow::bail!(\"MCP resource URI '{uri}' was not advertised by server '{server_name}'\");\n        }\n        let timeout = conn.config().effective_read_timeout(&global_timeouts);\n        conn.read_resource(uri, timeout).await\n    }\n\n    /// Get a prompt from a specific server\n    pub async fn get_prompt(\n        &mut self,\n        server_name: &str,\n        prompt_name: &str,\n        arguments: serde_json::Value,\n    ) -> Result<serde_json::Value> {\n        let global_timeouts = self.config.timeouts;\n        let conn = self.get_or_connect(server_name).await?;\n        if !conn\n            .prompts()\n            .iter()\n            .any(|prompt| prompt.name == prompt_name)","sourceCodeStart":2968,"sourceCodeEnd":3004,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/mcp.rs#L2968-L3004","documentation":"read_resource_advertised (the guarded read path) first fetches the connection's advertised catalog and checks the requested uri against both literal resource URIs and resource templates (resource_uri_matches_template). Only URIs the server advertised in resources/list or resources/templates/list are allowed through; anything else is rejected before a read is sent, so the server never receives a request for an unadvertised URI. This prevents authority smuggling via crafted URIs.","triggerScenarios":"Calling the resource-read API with a hand-typed URI that has a typo, a URI built from a template with wrong substitutions (so it matches neither literal nor template), or a URI remembered from an older catalog after the server changed its resource set.","commonSituations":"The model or user constructs URIs instead of copying them from list_mcp_resources; template parameter formatting drift (missing percent-encoding, wrong case); server restarted with fewer resources while the client cached the old list; URI scheme differences (file:// vs path).","solutions":["List the advertised resources first (list_mcp_resources / conn.resources()) and use an exact advertised URI, or expand a template from resource_templates() verbatim.","If the catalog may be stale (server restarted or config reloaded), force a reconnect/refresh so resources/list is re-fetched, then retry with the fresh URI.","For template resources, validate your substitution against the template pattern before calling.","Fix typos and encoding — the check is exact-match on literals and pattern-match on templates."],"exampleFix":"// before\npool.read_resource_advertised(\"docs\", \"file:///proj/readme\").await?;\n// Err: 'MCP resource URI ... was not advertised' (server advertises project://proj/readme)\n\n// after\nlet uri = pool.get_or_connect(\"docs\").await?\n    .resources().iter().find(|r| r.uri.ends_with(\"readme\"))\n    .map(|r| r.uri.clone()).expect(\"readme resource\");\npool.read_resource_advertised(\"docs\", &uri).await?;","handlingStrategy":"validation","validationCode":"// Rust: verify the URI is advertised before reading\nfn advertised(pool: &McpPool, server: &str, uri: &str) -> Result<()> {\n    let conn = pool.get_or_connect(server).await?;\n    let lit = conn.resources().iter().any(|r| r.uri == uri);\n    let tpl = conn.resource_templates().iter()\n        .any(|t| resource_uri_matches_template(uri, &t.uri_template));\n    ensure!(lit || tpl, \"URI '{uri}' not advertised\");\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":"// Rust: on failure, refresh the catalog and retry with an advertised URI\nmatch pool.read_resource_advertised(server, uri).await {\n    Err(e) if e.to_string().contains(\"was not advertised\") => {\n        let fresh = pool.get_or_connect(server).await?.resources().to_vec();\n        // re-pick `uri` from `fresh`, then retry\n    }\n    o => o,\n}","preventionTips":["Always copy URIs from list_mcp_resources output instead of typing them.","Expand resource templates with exactly the parameters the template declares.","Re-list resources after server restarts or config reloads — catalogs go stale."],"tags":["mcp","resources","validation","uri"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}