What happens
With a policy of min-integrity: approved + approval-labels: ["<label>"], applying that label to an issue should promote it to approved so the agent can read it. This works when the issue is read over the REST proxy, but not when read via the issue_read MCP tool: the label is ignored, the issue stays none/unapproved, and the read is DIFC-filtered. Confirmed on v0.3.30 and v0.4.1, with github-mcp-server v1.4.0.
Root cause
guards/github-guard/rust-guard/src/labels/helpers.rs, extract_github_label_names (line 394) reads label.name from each element, assuming labels are REST objects ([{"name":"x"}]). But github-mcp-server's issue_read returns labels as bare strings (["x"]), so .get("name") is None, the function returns an empty list, no label matches, and promotion is skipped. The guard already handles one REST-vs-MCP shape difference (it enriches author_association when the MCP path omits it, helpers.rs:1928); the same problem for labels was never handled.
Reproduce
Take an issue authored by a non-collaborator (author_association: NONE) carrying the label approved-label. Run github-mcp-server v1.4.0 as backend, put the guard (awmg, unified mode, --guards-mode filter) in front with policy allow-only: { min-integrity: approved, approval-labels: ["approved-label"], repos: all }, then read the issue via the issue_read tool. The read is filtered even though the label is present: [DIFC-FILTERED] issue_read integrity_tags:["none:all","unapproved:all"] author_association:"NONE". Controls: removing the label still filters (correct baseline); adding the author to trusted-users returns it (author promotion works); reading the same issue through the REST proxy returns it (label promotion works on the REST path).
Unit test reproduction
#[test]
fn test_has_approval_label_matches_bare_string_label() {
let ctx = PolicyContext {
approval_labels: vec!["approved".to_string()],
..Default::default()
};
let item = serde_json::json!({"labels": ["approved"]}); // MCP server shape
assert!(has_approval_label(&item, &ctx));
}
Impact
Approval-label review gates are the documented way for us to admit below-approved (e.g. community-reported) content to an agent, so any workflow relying on approval-labels + the github toolset cannot read the labeled item. Since extract_github_label_names is shared, refusal-labels is broken the same way: a refusal label on a tool-read item is ignored and not demoted to none (the security-relevant direction). Present in v0.3.30 through v0.4.1.
What happens
With a policy of
min-integrity: approved+approval-labels: ["<label>"], applying that label to an issue should promote it toapprovedso the agent can read it. This works when the issue is read over the REST proxy, but not when read via theissue_readMCP tool: the label is ignored, the issue staysnone/unapproved, and the read is DIFC-filtered. Confirmed on v0.3.30 and v0.4.1, withgithub-mcp-serverv1.4.0.Root cause
guards/github-guard/rust-guard/src/labels/helpers.rs,extract_github_label_names(line 394) readslabel.namefrom each element, assuming labels are REST objects ([{"name":"x"}]). Butgithub-mcp-server'sissue_readreturns labels as bare strings (["x"]), so.get("name")isNone, the function returns an empty list, no label matches, and promotion is skipped. The guard already handles one REST-vs-MCP shape difference (it enrichesauthor_associationwhen the MCP path omits it, helpers.rs:1928); the same problem forlabelswas never handled.Reproduce
Take an issue authored by a non-collaborator (
author_association: NONE) carrying the labelapproved-label. Rungithub-mcp-serverv1.4.0 as backend, put the guard (awmg, unified mode,--guards-mode filter) in front with policyallow-only: { min-integrity: approved, approval-labels: ["approved-label"], repos: all }, then read the issue via theissue_readtool. The read is filtered even though the label is present:[DIFC-FILTERED] issue_read integrity_tags:["none:all","unapproved:all"] author_association:"NONE". Controls: removing the label still filters (correct baseline); adding the author totrusted-usersreturns it (author promotion works); reading the same issue through the REST proxy returns it (label promotion works on the REST path).Unit test reproduction
Impact
Approval-label review gates are the documented way for us to admit below-
approved(e.g. community-reported) content to an agent, so any workflow relying onapproval-labels+ thegithubtoolset cannot read the labeled item. Sinceextract_github_label_namesis shared,refusal-labelsis broken the same way: a refusal label on a tool-read item is ignored and not demoted tonone(the security-relevant direction). Present in v0.3.30 through v0.4.1.