rasuvaeff/yii3-mcp-rbac-bridge
Composer 安装命令:
composer require rasuvaeff/yii3-mcp-rbac-bridge
包简介
Per-user RBAC for MCP servers: yiisoft/rbac permissions on yii3-mcp tool calls, visibility filtering and session-identity binding
README 文档
README
Per-user authorization for rasuvaeff/yii3-mcp
servers over the Yii3 auth stack — the application-facing alternative to
OAuth 2.1: RBAC permissions enforced on every tools/call, permission-aware
tools/list filtering, and session-identity binding against session
hijacking.
Using an AI coding assistant? llms.txt contains a compact API reference you can share with the model. Contributors: see AGENTS.md.
Requirements
| Requirement | Version |
|---|---|
| PHP | 8.3 – 8.5 |
rasuvaeff/yii3-mcp |
^1.1 |
yiisoft/access |
^2.0 (bind AccessCheckerInterface to your RBAC manager) |
yiisoft/user |
^2.0 (identity of the current request) |
Installation
composer require rasuvaeff/yii3-mcp-rbac-bridge
The model: two auth layers
SharedSecretMiddleware (yii3-mcp) stays — it is machine auth: may this
MCP client talk to this endpoint at all. This bridge adds user auth: what
may the authenticated user behind the call actually do. Both layers run;
removing the shared secret does not follow from adding RBAC.
// config/routes.php — secret first (cheap fail-closed), then identity Route::methods(['POST', 'GET', 'DELETE', 'OPTIONS'], '/mcp') ->middleware(SharedSecretMiddleware::class) ->middleware(Authentication::class) // yiisoft/auth: token -> CurrentUser ->action(McpAction::class),
Usage
1. Declare permissions on tools
use Rasuvaeff\Yii3McpRbacBridge\RequiredPermission; final readonly class OrderTools { #[McpTool(name: 'order.status')] #[RequiredPermission('orders.view')] public function status(string $orderId): string { ... } #[McpTool(name: 'ping')] // no attribute = unrestricted public function ping(): string { ... } }
Restriction is explicit and per-tool: tools without a permission stay open
(behind the shared secret). #[RequiredPermission] on a method without
#[McpTool] fails the build — a permission that would never be enforced is
a bug, not a default. One tool name mapped to two different permissions by
attributes fails the build too (a silent last-one-wins would enforce an
arbitrary one); explicit overrides win by design.
Tool names: what the map keys must be
PermissionMap keys are tool names, and the bridge derives each one exactly as
yii3-mcp registers it — so list and call can never key off different names:
| Tool declaration | Registered name = map key |
|---|---|
#[McpTool(name: 'order.status')] |
order.status — the explicit name wins |
#[McpTool] on public function status() |
status — the method name |
#[McpTool] on public function __invoke() |
the class short name (e.g. RefundTool), not __invoke |
fromToolClasses() computes these keys for you. Only an explicit map (the
$overrides argument, or new PermissionMap([...])) is yours to key correctly —
for an invokable tool that key is the class short name:
new PermissionMap(['RefundTool' => 'orders.refund']); // invokable RefundTool::__invoke
A key that matches no registered tool is inert (the tool stays unrestricted), so keep explicit keys in sync with the tool names above.
2. Wire the bridge
// config/common/di/mcp-rbac.php use Rasuvaeff\Yii3McpRbacBridge\ { CurrentUserIdentitySource, IdentitySourceInterface, PermissionMap, }; return [ IdentitySourceInterface::class => CurrentUserIdentitySource::class, PermissionMap::class => static fn () => PermissionMap::fromToolClasses( [OrderTools::class], // same list as the `tools` params // ['order.status' => 'orders.admin'], // optional explicit overrides ), ];
// config/params.php 'rasuvaeff/yii3-mcp' => [ 'tools' => [OrderTools::class], 'interceptors' => [ SessionIdentityInterceptor::class, // outermost: binding before anything trusts the session RbacToolCallInterceptor::class, ], 'tool_visibility' => RbacToolVisibility::class, ],
AccessCheckerInterface comes from your RBAC setup (yiisoft/rbac manager
with rbac-php/rbac-db storage — see suggest).
What each piece does
| Class | Role |
|---|---|
RbacToolCallInterceptor |
rejects tools/call without the mapped permission (regular MCP tool error, fail-closed for guests) |
RbacToolVisibility |
hides the same tools from tools/list — list and call can never disagree (one PermissionMap) |
SessionIdentityInterceptor |
binds the MCP session to its first identity; a leaked Mcp-Session-Id presented with another user's token is rejected |
PermissionMap |
tool name → permission: #[RequiredPermission] scan + explicit overrides |
CurrentUserIdentitySource |
identity id from yiisoft/user CurrentUser (null = guest); implement IdentitySourceInterface for stdio/custom setups |
Security notes
- Session binding happens on the first
tools/call(the yii3-mcp interceptor chain does not seeinitialize). Betweeninitializeand the first call the session carries no identity — nothing is authorized in that window, so nothing is exposed; the binding covers every actual operation. - Guests are first-class: a guest binds the session as a guest and is denied
on every permission-mapped tool (
AccessCheckerInterfacereceivesnull). The internal guest marker cannot be forged by a literal"guest"user id. - Permission revocation applies on the next call (fail-closed). Live
notifications/tools/list_changedon revocation is not part of this version — the SDK exposes it only with an event dispatcher, which yii3-mcp's factory does not yet carry. - For stdio (
mcp:serve) there is no HTTP request: bindIdentitySourceInterfaceto an env-/config-driven implementation, or leave the shared secret as the only (machine) identity.
Examples
See examples/ — runs offline.
| Script | Shows | Needs server? |
|---|---|---|
rbac.php |
Filtered listing, allowed/denied calls, session binding | no |
Development
No PHP/Composer on the host — run in Docker via the composer:2 image:
docker run --rm -v "$PWD":/app -w /app composer:2 composer build
Or with Make: make build, make cs-fix, make psalm, make test.
License
BSD-3-Clause. See LICENSE.md.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 1
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: BSD-3-Clause
- 更新时间: 2026-07-07