fix(mcp): keep loading api_key rows projected before the layered ACL - #993

Merged
jarvis9443 merged 2 commits into
mainfrom
fix/mcp-acl-loader-tolerates-stale-blocks
Aug 19, 2026
Merged

fix(mcp): keep loading api_key rows projected before the layered ACL#993
jarvis9443 merged 2 commits into
mainfrom
fix/mcp-acl-loader-tolerates-stale-blocks

Conversation

@jarvis9443

Copy link
Copy Markdown
Contributor

Follow-up to #992, which made allow required on every MCP ACL layer.

Required at the type level turned out to be too strong for the read path. Documents projected before the layered shape carry mcp_access: {"mode": "inherit"} and no allow, and nothing re-emits an api_key whose stored shape did not otherwise change. Such a row failed to deserialize, and the loader skips a row it cannot represent — so the key stopped authenticating for every kind of traffic, not just MCP. That is a much worse failure than losing tool access, and it would not have healed on its own.

The runtime loader now defaults a missing allow to empty, so a stale row loads as a layer allowing nothing: fail-closed on MCP while the key keeps serving LLM and A2A traffic. The write path is unchanged — the strict schema adds allow to required on both McpPolicy and the key's McpAccess, so neither a resources file nor the admin API can leave it out, and the published schemas/resources/*.json still document it as required. That is the usual split in this crate: the strict schema forbids, the lenient loader tolerates.

The control-plane side re-emits both collections once after its migration (api7/AISIX-Cloud#1335), so the stale documents are replaced rather than merely tolerated. This change is what keeps a key alive in the window before that runs.

A document written under the previous shape carries mcp_access.mode and
no allow list. With allow required at the type level that row failed to
deserialize, and the loader skips a row it cannot represent — so the key
stopped authenticating for every kind of traffic, not just MCP.
The runtime loader now defaults a missing allow to empty, which resolves
to a layer allowing nothing: fail-closed on MCP while the key keeps
working elsewhere. The write path is unchanged — the strict schema adds
allow to required on both layers, so neither a resources file nor the
admin API can leave it out.
@coderabbitai

coderabbitaiBot commented Aug 19, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

This review includes 5 billable files. This on-demand review is free during your promotion.

Your included review limit has been reached. Run @coderabbitai review --use-credits to review the latest changes using usage credits.

  • Run review — free
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2bf1b44a-554f-4b57-ad8c-75edb6919a87

📥 Commits

Reviewing files that changed from the base of the PR and between 7a9fc86 and aa9e383.

📒 Files selected for processing (5)
  • CLAUDE.md
  • crates/aisix-core/src/models/mcp_policy.rs
  • crates/aisix-core/src/models/schema.rs
  • schemas/resources/api_key.schema.json
  • schemas/resources/mcp_policy.schema.json

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/mcp-acl-loader-tolerates-stale-blocks

Comment @coderabbitai help to get the list of available commands.

…ired
The loader skips a row it cannot deserialize, and a skipped api_key row
stops authenticating every kind of traffic — so requiredness belongs in
the strict schema, with a fail-closed serde default on the struct.
@jarvis9443
jarvis9443 merged commit 86dd01e into mainAug 19, 2026
14 checks passed
@jarvis9443
jarvis9443 deleted the fix/mcp-acl-loader-tolerates-stale-blocks branch August 19, 2026 07:27
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@jarvis9443
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(mcp): keep loading api_key rows projected before the layered ACL - #993

Merged
jarvis9443 merged 2 commits into
mainfrom
fix/mcp-acl-loader-tolerates-stale-blocks
Aug 19, 2026
Merged

fix(mcp): keep loading api_key rows projected before the layered ACL#993
jarvis9443 merged 2 commits into
mainfrom
fix/mcp-acl-loader-tolerates-stale-blocks

Conversation

@jarvis9443

Copy link
Copy Markdown
Contributor

Follow-up to #992, which made allow required on every MCP ACL layer.

Required at the type level turned out to be too strong for the read path. Documents projected before the layered shape carry mcp_access: {"mode": "inherit"} and no allow, and nothing re-emits an api_key whose stored shape did not otherwise change. Such a row failed to deserialize, and the loader skips a row it cannot represent — so the key stopped authenticating for every kind of traffic, not just MCP. That is a much worse failure than losing tool access, and it would not have healed on its own.

The runtime loader now defaults a missing allow to empty, so a stale row loads as a layer allowing nothing: fail-closed on MCP while the key keeps serving LLM and A2A traffic. The write path is unchanged — the strict schema adds allow to required on both McpPolicy and the key's McpAccess, so neither a resources file nor the admin API can leave it out, and the published schemas/resources/*.json still document it as required. That is the usual split in this crate: the strict schema forbids, the lenient loader tolerates.

The control-plane side re-emits both collections once after its migration (api7/AISIX-Cloud#1335), so the stale documents are replaced rather than merely tolerated. This change is what keeps a key alive in the window before that runs.

A document written under the previous shape carries mcp_access.mode and
no allow list. With allow required at the type level that row failed to
deserialize, and the loader skips a row it cannot represent — so the key
stopped authenticating for every kind of traffic, not just MCP.
The runtime loader now defaults a missing allow to empty, which resolves
to a layer allowing nothing: fail-closed on MCP while the key keeps
working elsewhere. The write path is unchanged — the strict schema adds
allow to required on both layers, so neither a resources file nor the
admin API can leave it out.
@coderabbitai

coderabbitaiBot commented Aug 19, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

This review includes 5 billable files. This on-demand review is free during your promotion.

Your included review limit has been reached. Run @coderabbitai review --use-credits to review the latest changes using usage credits.

  • Run review — free
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2bf1b44a-554f-4b57-ad8c-75edb6919a87

📥 Commits

Reviewing files that changed from the base of the PR and between 7a9fc86 and aa9e383.

📒 Files selected for processing (5)
  • CLAUDE.md
  • crates/aisix-core/src/models/mcp_policy.rs
  • crates/aisix-core/src/models/schema.rs
  • schemas/resources/api_key.schema.json
  • schemas/resources/mcp_policy.schema.json

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/mcp-acl-loader-tolerates-stale-blocks

Comment @coderabbitai help to get the list of available commands.

…ired
The loader skips a row it cannot deserialize, and a skipped api_key row
stops authenticating every kind of traffic — so requiredness belongs in
the strict schema, with a fail-closed serde default on the struct.
@jarvis9443
jarvis9443 merged commit 86dd01e into mainAug 19, 2026
14 checks passed
@jarvis9443
jarvis9443 deleted the fix/mcp-acl-loader-tolerates-stale-blocks branch August 19, 2026 07:27
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@jarvis9443
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(mcp): keep loading api_key rows projected before the layered ACL - #993

Merged
jarvis9443 merged 2 commits into
mainfrom
fix/mcp-acl-loader-tolerates-stale-blocks
Aug 19, 2026
Merged

fix(mcp): keep loading api_key rows projected before the layered ACL#993
jarvis9443 merged 2 commits into
mainfrom
fix/mcp-acl-loader-tolerates-stale-blocks

Conversation

@jarvis9443

Copy link
Copy Markdown
Contributor

Follow-up to #992, which made allow required on every MCP ACL layer.

Required at the type level turned out to be too strong for the read path. Documents projected before the layered shape carry mcp_access: {"mode": "inherit"} and no allow, and nothing re-emits an api_key whose stored shape did not otherwise change. Such a row failed to deserialize, and the loader skips a row it cannot represent — so the key stopped authenticating for every kind of traffic, not just MCP. That is a much worse failure than losing tool access, and it would not have healed on its own.

The runtime loader now defaults a missing allow to empty, so a stale row loads as a layer allowing nothing: fail-closed on MCP while the key keeps serving LLM and A2A traffic. The write path is unchanged — the strict schema adds allow to required on both McpPolicy and the key's McpAccess, so neither a resources file nor the admin API can leave it out, and the published schemas/resources/*.json still document it as required. That is the usual split in this crate: the strict schema forbids, the lenient loader tolerates.

The control-plane side re-emits both collections once after its migration (api7/AISIX-Cloud#1335), so the stale documents are replaced rather than merely tolerated. This change is what keeps a key alive in the window before that runs.

A document written under the previous shape carries mcp_access.mode and
no allow list. With allow required at the type level that row failed to
deserialize, and the loader skips a row it cannot represent — so the key
stopped authenticating for every kind of traffic, not just MCP.
The runtime loader now defaults a missing allow to empty, which resolves
to a layer allowing nothing: fail-closed on MCP while the key keeps
working elsewhere. The write path is unchanged — the strict schema adds
allow to required on both layers, so neither a resources file nor the
admin API can leave it out.
@coderabbitai

coderabbitaiBot commented Aug 19, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

This review includes 5 billable files. This on-demand review is free during your promotion.

Your included review limit has been reached. Run @coderabbitai review --use-credits to review the latest changes using usage credits.

  • Run review — free
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2bf1b44a-554f-4b57-ad8c-75edb6919a87

📥 Commits

Reviewing files that changed from the base of the PR and between 7a9fc86 and aa9e383.

📒 Files selected for processing (5)
  • CLAUDE.md
  • crates/aisix-core/src/models/mcp_policy.rs
  • crates/aisix-core/src/models/schema.rs
  • schemas/resources/api_key.schema.json
  • schemas/resources/mcp_policy.schema.json

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/mcp-acl-loader-tolerates-stale-blocks

Comment @coderabbitai help to get the list of available commands.

…ired
The loader skips a row it cannot deserialize, and a skipped api_key row
stops authenticating every kind of traffic — so requiredness belongs in
the strict schema, with a fail-closed serde default on the struct.
@jarvis9443
jarvis9443 merged commit 86dd01e into mainAug 19, 2026
14 checks passed
@jarvis9443
jarvis9443 deleted the fix/mcp-acl-loader-tolerates-stale-blocks branch August 19, 2026 07:27
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@jarvis9443
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(mcp): keep loading api_key rows projected before the layered ACL - #993

Merged
jarvis9443 merged 2 commits into
mainfrom
fix/mcp-acl-loader-tolerates-stale-blocks
Aug 19, 2026
Merged

fix(mcp): keep loading api_key rows projected before the layered ACL#993
jarvis9443 merged 2 commits into
mainfrom
fix/mcp-acl-loader-tolerates-stale-blocks

Conversation

@jarvis9443

Copy link
Copy Markdown
Contributor

Follow-up to #992, which made allow required on every MCP ACL layer.

Required at the type level turned out to be too strong for the read path. Documents projected before the layered shape carry mcp_access: {"mode": "inherit"} and no allow, and nothing re-emits an api_key whose stored shape did not otherwise change. Such a row failed to deserialize, and the loader skips a row it cannot represent — so the key stopped authenticating for every kind of traffic, not just MCP. That is a much worse failure than losing tool access, and it would not have healed on its own.

The runtime loader now defaults a missing allow to empty, so a stale row loads as a layer allowing nothing: fail-closed on MCP while the key keeps serving LLM and A2A traffic. The write path is unchanged — the strict schema adds allow to required on both McpPolicy and the key's McpAccess, so neither a resources file nor the admin API can leave it out, and the published schemas/resources/*.json still document it as required. That is the usual split in this crate: the strict schema forbids, the lenient loader tolerates.

The control-plane side re-emits both collections once after its migration (api7/AISIX-Cloud#1335), so the stale documents are replaced rather than merely tolerated. This change is what keeps a key alive in the window before that runs.

A document written under the previous shape carries mcp_access.mode and
no allow list. With allow required at the type level that row failed to
deserialize, and the loader skips a row it cannot represent — so the key
stopped authenticating for every kind of traffic, not just MCP.
The runtime loader now defaults a missing allow to empty, which resolves
to a layer allowing nothing: fail-closed on MCP while the key keeps
working elsewhere. The write path is unchanged — the strict schema adds
allow to required on both layers, so neither a resources file nor the
admin API can leave it out.
@coderabbitai

coderabbitaiBot commented Aug 19, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

This review includes 5 billable files. This on-demand review is free during your promotion.

Your included review limit has been reached. Run @coderabbitai review --use-credits to review the latest changes using usage credits.

  • Run review — free
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2bf1b44a-554f-4b57-ad8c-75edb6919a87

📥 Commits

Reviewing files that changed from the base of the PR and between 7a9fc86 and aa9e383.

📒 Files selected for processing (5)
  • CLAUDE.md
  • crates/aisix-core/src/models/mcp_policy.rs
  • crates/aisix-core/src/models/schema.rs
  • schemas/resources/api_key.schema.json
  • schemas/resources/mcp_policy.schema.json

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/mcp-acl-loader-tolerates-stale-blocks

Comment @coderabbitai help to get the list of available commands.

…ired
The loader skips a row it cannot deserialize, and a skipped api_key row
stops authenticating every kind of traffic — so requiredness belongs in
the strict schema, with a fail-closed serde default on the struct.
@jarvis9443
jarvis9443 merged commit 86dd01e into mainAug 19, 2026
14 checks passed
@jarvis9443
jarvis9443 deleted the fix/mcp-acl-loader-tolerates-stale-blocks branch August 19, 2026 07:27
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@jarvis9443
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(mcp): keep loading api_key rows projected before the layered ACL - #993

Merged
jarvis9443 merged 2 commits into
mainfrom
fix/mcp-acl-loader-tolerates-stale-blocks
Aug 19, 2026
Merged

fix(mcp): keep loading api_key rows projected before the layered ACL#993
jarvis9443 merged 2 commits into
mainfrom
fix/mcp-acl-loader-tolerates-stale-blocks

Conversation

@jarvis9443

Copy link
Copy Markdown
Contributor

Follow-up to #992, which made allow required on every MCP ACL layer.

Required at the type level turned out to be too strong for the read path. Documents projected before the layered shape carry mcp_access: {"mode": "inherit"} and no allow, and nothing re-emits an api_key whose stored shape did not otherwise change. Such a row failed to deserialize, and the loader skips a row it cannot represent — so the key stopped authenticating for every kind of traffic, not just MCP. That is a much worse failure than losing tool access, and it would not have healed on its own.

The runtime loader now defaults a missing allow to empty, so a stale row loads as a layer allowing nothing: fail-closed on MCP while the key keeps serving LLM and A2A traffic. The write path is unchanged — the strict schema adds allow to required on both McpPolicy and the key's McpAccess, so neither a resources file nor the admin API can leave it out, and the published schemas/resources/*.json still document it as required. That is the usual split in this crate: the strict schema forbids, the lenient loader tolerates.

The control-plane side re-emits both collections once after its migration (api7/AISIX-Cloud#1335), so the stale documents are replaced rather than merely tolerated. This change is what keeps a key alive in the window before that runs.

A document written under the previous shape carries mcp_access.mode and
no allow list. With allow required at the type level that row failed to
deserialize, and the loader skips a row it cannot represent — so the key
stopped authenticating for every kind of traffic, not just MCP.
The runtime loader now defaults a missing allow to empty, which resolves
to a layer allowing nothing: fail-closed on MCP while the key keeps
working elsewhere. The write path is unchanged — the strict schema adds
allow to required on both layers, so neither a resources file nor the
admin API can leave it out.
@coderabbitai

coderabbitaiBot commented Aug 19, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

This review includes 5 billable files. This on-demand review is free during your promotion.

Your included review limit has been reached. Run @coderabbitai review --use-credits to review the latest changes using usage credits.

  • Run review — free
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2bf1b44a-554f-4b57-ad8c-75edb6919a87

📥 Commits

Reviewing files that changed from the base of the PR and between 7a9fc86 and aa9e383.

📒 Files selected for processing (5)
  • CLAUDE.md
  • crates/aisix-core/src/models/mcp_policy.rs
  • crates/aisix-core/src/models/schema.rs
  • schemas/resources/api_key.schema.json
  • schemas/resources/mcp_policy.schema.json

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/mcp-acl-loader-tolerates-stale-blocks

Comment @coderabbitai help to get the list of available commands.

…ired
The loader skips a row it cannot deserialize, and a skipped api_key row
stops authenticating every kind of traffic — so requiredness belongs in
the strict schema, with a fail-closed serde default on the struct.
@jarvis9443
jarvis9443 merged commit 86dd01e into mainAug 19, 2026
14 checks passed
@jarvis9443
jarvis9443 deleted the fix/mcp-acl-loader-tolerates-stale-blocks branch August 19, 2026 07:27
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@jarvis9443
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(mcp): keep loading api_key rows projected before the layered ACL - #993

Merged
jarvis9443 merged 2 commits into
mainfrom
fix/mcp-acl-loader-tolerates-stale-blocks
Aug 19, 2026
Merged

fix(mcp): keep loading api_key rows projected before the layered ACL#993
jarvis9443 merged 2 commits into
mainfrom
fix/mcp-acl-loader-tolerates-stale-blocks

Conversation

@jarvis9443

Copy link
Copy Markdown
Contributor

Follow-up to #992, which made allow required on every MCP ACL layer.

Required at the type level turned out to be too strong for the read path. Documents projected before the layered shape carry mcp_access: {"mode": "inherit"} and no allow, and nothing re-emits an api_key whose stored shape did not otherwise change. Such a row failed to deserialize, and the loader skips a row it cannot represent — so the key stopped authenticating for every kind of traffic, not just MCP. That is a much worse failure than losing tool access, and it would not have healed on its own.

The runtime loader now defaults a missing allow to empty, so a stale row loads as a layer allowing nothing: fail-closed on MCP while the key keeps serving LLM and A2A traffic. The write path is unchanged — the strict schema adds allow to required on both McpPolicy and the key's McpAccess, so neither a resources file nor the admin API can leave it out, and the published schemas/resources/*.json still document it as required. That is the usual split in this crate: the strict schema forbids, the lenient loader tolerates.

The control-plane side re-emits both collections once after its migration (api7/AISIX-Cloud#1335), so the stale documents are replaced rather than merely tolerated. This change is what keeps a key alive in the window before that runs.

A document written under the previous shape carries mcp_access.mode and
no allow list. With allow required at the type level that row failed to
deserialize, and the loader skips a row it cannot represent — so the key
stopped authenticating for every kind of traffic, not just MCP.
The runtime loader now defaults a missing allow to empty, which resolves
to a layer allowing nothing: fail-closed on MCP while the key keeps
working elsewhere. The write path is unchanged — the strict schema adds
allow to required on both layers, so neither a resources file nor the
admin API can leave it out.
@coderabbitai

coderabbitaiBot commented Aug 19, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

This review includes 5 billable files. This on-demand review is free during your promotion.

Your included review limit has been reached. Run @coderabbitai review --use-credits to review the latest changes using usage credits.

  • Run review — free
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2bf1b44a-554f-4b57-ad8c-75edb6919a87

📥 Commits

Reviewing files that changed from the base of the PR and between 7a9fc86 and aa9e383.

📒 Files selected for processing (5)
  • CLAUDE.md
  • crates/aisix-core/src/models/mcp_policy.rs
  • crates/aisix-core/src/models/schema.rs
  • schemas/resources/api_key.schema.json
  • schemas/resources/mcp_policy.schema.json

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/mcp-acl-loader-tolerates-stale-blocks

Comment @coderabbitai help to get the list of available commands.

…ired
The loader skips a row it cannot deserialize, and a skipped api_key row
stops authenticating every kind of traffic — so requiredness belongs in
the strict schema, with a fail-closed serde default on the struct.
@jarvis9443
jarvis9443 merged commit 86dd01e into mainAug 19, 2026
14 checks passed
@jarvis9443
jarvis9443 deleted the fix/mcp-acl-loader-tolerates-stale-blocks branch August 19, 2026 07:27
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@jarvis9443
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(mcp): keep loading api_key rows projected before the layered ACL - #993

Merged
jarvis9443 merged 2 commits into
mainfrom
fix/mcp-acl-loader-tolerates-stale-blocks
Aug 19, 2026
Merged

fix(mcp): keep loading api_key rows projected before the layered ACL#993
jarvis9443 merged 2 commits into
mainfrom
fix/mcp-acl-loader-tolerates-stale-blocks

Conversation

@jarvis9443

Copy link
Copy Markdown
Contributor

Follow-up to #992, which made allow required on every MCP ACL layer.

Required at the type level turned out to be too strong for the read path. Documents projected before the layered shape carry mcp_access: {"mode": "inherit"} and no allow, and nothing re-emits an api_key whose stored shape did not otherwise change. Such a row failed to deserialize, and the loader skips a row it cannot represent — so the key stopped authenticating for every kind of traffic, not just MCP. That is a much worse failure than losing tool access, and it would not have healed on its own.

The runtime loader now defaults a missing allow to empty, so a stale row loads as a layer allowing nothing: fail-closed on MCP while the key keeps serving LLM and A2A traffic. The write path is unchanged — the strict schema adds allow to required on both McpPolicy and the key's McpAccess, so neither a resources file nor the admin API can leave it out, and the published schemas/resources/*.json still document it as required. That is the usual split in this crate: the strict schema forbids, the lenient loader tolerates.

The control-plane side re-emits both collections once after its migration (api7/AISIX-Cloud#1335), so the stale documents are replaced rather than merely tolerated. This change is what keeps a key alive in the window before that runs.

A document written under the previous shape carries mcp_access.mode and
no allow list. With allow required at the type level that row failed to
deserialize, and the loader skips a row it cannot represent — so the key
stopped authenticating for every kind of traffic, not just MCP.
The runtime loader now defaults a missing allow to empty, which resolves
to a layer allowing nothing: fail-closed on MCP while the key keeps
working elsewhere. The write path is unchanged — the strict schema adds
allow to required on both layers, so neither a resources file nor the
admin API can leave it out.
@coderabbitai

coderabbitaiBot commented Aug 19, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

This review includes 5 billable files. This on-demand review is free during your promotion.

Your included review limit has been reached. Run @coderabbitai review --use-credits to review the latest changes using usage credits.

  • Run review — free
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2bf1b44a-554f-4b57-ad8c-75edb6919a87

📥 Commits

Reviewing files that changed from the base of the PR and between 7a9fc86 and aa9e383.

📒 Files selected for processing (5)
  • CLAUDE.md
  • crates/aisix-core/src/models/mcp_policy.rs
  • crates/aisix-core/src/models/schema.rs
  • schemas/resources/api_key.schema.json
  • schemas/resources/mcp_policy.schema.json

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/mcp-acl-loader-tolerates-stale-blocks

Comment @coderabbitai help to get the list of available commands.

…ired
The loader skips a row it cannot deserialize, and a skipped api_key row
stops authenticating every kind of traffic — so requiredness belongs in
the strict schema, with a fail-closed serde default on the struct.
@jarvis9443
jarvis9443 merged commit 86dd01e into mainAug 19, 2026
14 checks passed
@jarvis9443
jarvis9443 deleted the fix/mcp-acl-loader-tolerates-stale-blocks branch August 19, 2026 07:27
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@jarvis9443
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(mcp): keep loading api_key rows projected before the layered ACL - #993

Merged
jarvis9443 merged 2 commits into
mainfrom
fix/mcp-acl-loader-tolerates-stale-blocks
Aug 19, 2026
Merged

fix(mcp): keep loading api_key rows projected before the layered ACL#993
jarvis9443 merged 2 commits into
mainfrom
fix/mcp-acl-loader-tolerates-stale-blocks

Conversation

@jarvis9443

Copy link
Copy Markdown
Contributor

Follow-up to #992, which made allow required on every MCP ACL layer.

Required at the type level turned out to be too strong for the read path. Documents projected before the layered shape carry mcp_access: {"mode": "inherit"} and no allow, and nothing re-emits an api_key whose stored shape did not otherwise change. Such a row failed to deserialize, and the loader skips a row it cannot represent — so the key stopped authenticating for every kind of traffic, not just MCP. That is a much worse failure than losing tool access, and it would not have healed on its own.

The runtime loader now defaults a missing allow to empty, so a stale row loads as a layer allowing nothing: fail-closed on MCP while the key keeps serving LLM and A2A traffic. The write path is unchanged — the strict schema adds allow to required on both McpPolicy and the key's McpAccess, so neither a resources file nor the admin API can leave it out, and the published schemas/resources/*.json still document it as required. That is the usual split in this crate: the strict schema forbids, the lenient loader tolerates.

The control-plane side re-emits both collections once after its migration (api7/AISIX-Cloud#1335), so the stale documents are replaced rather than merely tolerated. This change is what keeps a key alive in the window before that runs.

A document written under the previous shape carries mcp_access.mode and
no allow list. With allow required at the type level that row failed to
deserialize, and the loader skips a row it cannot represent — so the key
stopped authenticating for every kind of traffic, not just MCP.
The runtime loader now defaults a missing allow to empty, which resolves
to a layer allowing nothing: fail-closed on MCP while the key keeps
working elsewhere. The write path is unchanged — the strict schema adds
allow to required on both layers, so neither a resources file nor the
admin API can leave it out.
@coderabbitai

coderabbitaiBot commented Aug 19, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

This review includes 5 billable files. This on-demand review is free during your promotion.

Your included review limit has been reached. Run @coderabbitai review --use-credits to review the latest changes using usage credits.

  • Run review — free
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2bf1b44a-554f-4b57-ad8c-75edb6919a87

📥 Commits

Reviewing files that changed from the base of the PR and between 7a9fc86 and aa9e383.

📒 Files selected for processing (5)
  • CLAUDE.md
  • crates/aisix-core/src/models/mcp_policy.rs
  • crates/aisix-core/src/models/schema.rs
  • schemas/resources/api_key.schema.json
  • schemas/resources/mcp_policy.schema.json

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/mcp-acl-loader-tolerates-stale-blocks

Comment @coderabbitai help to get the list of available commands.

…ired
The loader skips a row it cannot deserialize, and a skipped api_key row
stops authenticating every kind of traffic — so requiredness belongs in
the strict schema, with a fail-closed serde default on the struct.
@jarvis9443
jarvis9443 merged commit 86dd01e into mainAug 19, 2026
14 checks passed
@jarvis9443
jarvis9443 deleted the fix/mcp-acl-loader-tolerates-stale-blocks branch August 19, 2026 07:27
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@jarvis9443