NeuralMind v4.6.1 — the MCP server applies your security settings
| Type: Patch release | Theme: MCP access control |
security.roles and security.rate_limit in neuralmind-backend.yaml now
apply to the MCP server. Through v4.6.0 the server built its security manager
without reading that file, so every call got the default role policy and a
limit of 60 calls a minute, whatever the YAML said. A caller could declare
admin and reach every tool, even in a project whose policy left admin out,
as the Security Guide
recommends.
If your project has no security: block in neuralmind-backend.yaml, nothing
changes: no NeuralMind command writes one, and the defaults are the same as in
v4.6.0.
What changed
security.rolesreplaces the default policy. Each role gets exactly the tools listed for it (or"*"for all of them), and a role the policy doesn’t list gets none. Callers still declare their own role, so leavingadminout of the policy is what caps what any caller can reach. A refused call returnscode: "security_denied"withreason: "rbac".security.rate_limitsets the limiter (max_callsperwindow_seconds, default 60 per 60). It still counts per declared actor, so it stops a runaway agent, not a caller that changes its actor name. A call over the limit returnsreason: "rate_limit". Every denial is written to.neuralmind/audit_events.jsonl, with its reason.- A policy that parses but is wrong fails closed.
roles: {}grants nothing, instead of falling back to the defaults. If thesecurity:section,rolesorrate_limithas a value that isn’t a mapping, or a limit isn’t a whole number (orwindow_secondsis under 1), the server refuses every MCP call withcode: "security_denied",reason: "config", and an error naming the setting, instead of guessing at a looser policy. A role whose value is neither a list nor"*"gets no tools. - Two mistakes still fall back to the defaults,
adminincluded. A file that doesn’t parse (a YAML syntax error) is read as no configuration, and asecurity:orroles:key left empty (null) is read as no policy, as israte_limit: nullfor the limiter. After writing a policy, check that it holds: call a tool you left out withrole: "admin"and expectsecurity_denied. - The policy is read once per MCP server process. The server keeps it for
each project until it exits, so an edit to
security:takes effect when the MCP server restarts (a new agent session, or reconnecting the server). - The docs describe it as working.
SECURITY.md, the Security Guide, the Deployment Guide, the FAQ and risk R-03 had warned that these settings were ignored; they now show how to use them. The v4.5.1 release notes said asecurity.rolespolicy replaced the defaults; through the MCP server it didn’t until this release.
Example, from the Security Guide:
# neuralmind-backend.yaml, in the project root
security:
roles:
builder: [neuralmind_wakeup, neuralmind_query, neuralmind_search, neuralmind_skeleton, neuralmind_build]
reader: [neuralmind_wakeup, neuralmind_query, neuralmind_search, neuralmind_skeleton]
rate_limit:
max_calls: 60
window_seconds: 60
What the agent actually sees post-install
Only in a project whose neuralmind-backend.yaml has a security: block.
Without one, every agent sees exactly what it saw in v4.6.0.
| Agent | Before | After |
|---|---|---|
| Claude Code (MCP + hooks) | Every MCP tool the default policy grants, whatever security.roles said; 60 calls a minute |
Only the tools security.roles grants the role it calls with (builder when it names none); a tool outside that returns security_denied with reason: "rbac", and every tool returns reason: "config" while the security: section has a value of the wrong type. Hooks don’t go through the MCP server and are unaffected |
| Cursor / Cline / Continue (MCP) | Same as Claude Code | Same as Claude Code |
| Generic MCP client | role: "admin" reached every tool |
role: "admin" reaches only what the policy grants admin; nothing, if the policy doesn’t list it |
Upgrade notes
pip install -U neuralmind. No rebuild needed.- If your
neuralmind-backend.yamlsetssecurity.roles, check it before upgrading. It now takes effect, and it replaces the defaults rather than adding to them:- An agent that names no role is
builder. If the policy doesn’t listbuilder, that agent gets no tools at all. - A tool you don’t list for a role is denied to that role, including tools
the default policy granted, such as
neuralmind_memory_search,neuralmind_memory_timelineandneuralmind_memory_get. The default policy isDEFAULT_ROLE_POLICYinneuralmind/mcp_security.py; copy from it.
- An agent that names no role is
- If it sets
security.rate_limit, that limit now applies instead of 60 calls a minute. - If every MCP call comes back with
reason: "config", thesecurity:section has a value of the wrong type; the error names the setting to fix. Fix it, then restart the MCP server: calls keep failing until it rereads the file. v4.6.0 ignored the section, so a mistake in it went unnoticed until now. - If a tool you left out still answers, the file didn’t parse or the
security:orroles:key is empty; both mean the defaults apply. - To get v4.6.0’s behaviour back, delete the
security:block and restart the MCP server.
Related
- Security Guide: capping what a caller can claim · access control
- Deployment Guide · FAQ
- Previous release: v4.6.0