feat(mcp): server-level wing access control via --allowed-wings / --blocked-wings - #1366
Open
brodheadw wants to merge 3 commits into
Open
feat(mcp): server-level wing access control via --allowed-wings / --blocked-wings#1366brodheadw wants to merge 3 commits into
brodheadw wants to merge 3 commits into
Conversation
brodheadw
force-pushed
the
feat/mcp-wing-policy-rebased
branch
from
May 5, 2026 19:39
f4363a2 to
2af5575
Compare
Author
|
Open to discussing further with maintainers. I know this might read as a significant change, but it's totally opt-in, no flags, every agent still has full access. Felt necessary for cleanly managing multi-agent access to memory, especially with overlapping projects. My email's in my profile. |
Add --allowed-wings / --blocked-wings flags to mempalace-mcp. Each agent's MCP server invocation carries its own policy; all instances share the same ~/.mempalace data on disk but enforce isolation server-side per process. Blocklist takes precedence over allowlist. Default is no-op when neither flag is set. Enforcement patterns across 18 wing-addressable tools: - Hard block (access_denied) on explicit-wing requests: search, list_rooms, find_tunnels, follow_tunnels, add/get/list/update/ delete_drawer, create/list/delete_tunnel, diary_read/write. - Soft filter on aggregates: status, list_wings, get_taxonomy, list_rooms (unfiltered), list_drawers (unfiltered) silently omit blocked wings from results. - Post-filter on result arrays: search, check_duplicate, diary_read (spans-all-wings), list_tunnels (either endpoint), follow_tunnels (destination wing) strip blocked-wing hits before returning. - ID lookup -> policy: get/update/delete_drawer fetch metadata first; delete_tunnel scans tunnels.json to enforce on both endpoints; update_drawer also blocks moves *into* a blocked wing. Not covered (inline TODOs): traverse_graph, graph_stats - need policy-aware traversal in palace_graph.py. 18 new tests covering policy logic, soft filter, hard block, post-filter paths, ID-based lookups across drawer + tunnel families.
The existing rule matches only the literal '.coverage' file. pytest-cov writes additional per-process data files named '.coverage.<host>.<pid>.<rand>' which slipped past the rule.
brodheadw
force-pushed
the
feat/mcp-wing-policy-rebased
branch
from
May 11, 2026 16:04
414f593 to
430c9b7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
WingPolicyclass enforces wing-level access control on every MCP tool that touches palace data.mempalace-mcp:--allowed-wings(allowlist) and--blocked-wings(denylist; takes precedence).Problem
A user running multiple agents shouldn't have to trust every agent with every wing. Today, any agent with the
mempalaceMCP server configured can read or write any wing the server has access to — there's no way to scope an agent to its own project's wing, hide a financial wing from a general assistant, or give a "master" agent broader access than its sub-agents.WingPolicyadds that scoping at the server-process level.Change
mempalace-mcpnow accepts:--allowed-wings WINGS— comma-separated allowlist; all unlisted wings denied--blocked-wings WINGS— comma-separated denylist; takes precedence over allowlistEach agent's MCP server invocation carries its own policy:
All instances share
~/.mempalace/data on disk; isolation is enforced server-side per process.Enforcement matrix
access_deniedon explicit-wing requests)list_rooms,search,find_tunnels,add_drawer,diary_read,diary_write,list_drawers,create_tunnel,list_tunnels,follow_tunnelsstatus,list_wings,get_taxonomy,list_rooms(unfiltered),list_drawers(unfiltered)search,check_duplicate,diary_read(spans-all-wings),list_tunnels(either endpoint),follow_tunnels(destination)get_drawer,update_drawer(old AND new wing),delete_drawer,delete_tunnel(both endpoints)Also bundled: a one-line
.gitignorefix for per-process.coverage.*artifacts that slipped past the existing literal.coveragerule.Tests
WingPolicyallowlist + blocklist + precedence logicstatus,list_wings,get_taxonomylist_roomsunfiltered drops rooms unique to blocked wingssearchpost-filter drops blocked hits when no wing arg givencheck_duplicatesuppresses blocked-wing matches (no existence-leak)delete_drawer/update_drawer/get_drawerenforce via ID lookupupdate_draweralso blocks moves into a blocked wingcreate_tunnelblocks when either endpoint is blockedlist_tunnelsdrops tunnels where either endpoint is in a blocked wingdelete_tunnelenforces on either endpoint via tunnel-id lookupfollow_tunnelshard-blocks origin and post-filters destinationsruff format+ruff checkclean. All 18 newTestWingPolicytests pass; rebased onto currentmain.What's NOT in this PR
traverse_graph/graph_stats— full enforcement requires policy-aware traversal insidepalace_graph.py. Inline notes added to both tools' docstrings flagging the gap. Happy to do as a follow-up.kg_query,kg_add,kg_invalidate,kg_timeline,kg_stats) — the knowledge graph is entity-keyed, not wing-keyed. Different scoping model.WingPolicyoperates at the MCP server process level. Hosts that spawn sub-agents which share the parent's MCP connection will see those sub-agents inherit the parent's policy. True per-sub-agent scoping requires the host to launch each sub-agent with its ownmempalace-mcpinvocation — a host-side concern, not a server-side one. Worth a note in any user docs that mention per-agent policies.