feat(mcpd+cli+mcplocal): wire visibility filter through routes, CLI, registrar (v7 Stage 2)
Stage 1 added the schema + service predicate. This stage threads the filter through every surface that lists or fetches Llms/Agents: - mcpd routes: viewerFromRequest helper builds a Viewer from the request's RBAC scope. List endpoints rely on the existing preSerialization hook (now two-phase: name-scope first, visibility second). get-by-id/get-by-name routes pass the viewer to the service which 404s on hidden rows. - RBAC: AllowedScope gains `isAdmin` to distinguish a `*` cross-resource grant (admins skip visibility) from a plain `view:llms` grant (wildcard for RBAC, but visibility still applies). FastifyRequest augmentation updated. - VirtualLlmService.register accepts ownerId and stamps it on freshly created virtual rows; defaults visibility to 'private' on first create, leaves existing rows untouched on sticky reconnect. - AgentService.registerVirtualAgents mirrors the same defaults. - mcplocal: LlmProviderFileEntry / AgentFileEntry / RegistrarPublishedX carry visibility through to the register payload (default 'private'). - CLI: VISIBILITY column on `mcpctl get llm` and `mcpctl get agent`, `--visibility` flag on `mcpctl create llm` / `create agent`. YAML round-trip works because visibility passes through stripInternalFields unchanged (ownerId is already stripped). Completions regenerated. Tests: mcpd 908/908, mcplocal 731/731, cli 437/437. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -105,6 +105,14 @@ export interface LlmProviderFileEntry {
|
||||
* logical pool that auto-grows as more workers come online.
|
||||
*/
|
||||
poolName?: string;
|
||||
/**
|
||||
* v7: per-user RBAC scoping. mcplocal-published virtuals default to
|
||||
* 'private' on register — the publishing user owns the row and other
|
||||
* users don't see it without an explicit `view:llms:<name>` grant.
|
||||
* Set to 'public' here to opt into org-wide sharing for this
|
||||
* provider.
|
||||
*/
|
||||
visibility?: 'public' | 'private';
|
||||
}
|
||||
|
||||
export type WakeRecipe =
|
||||
@@ -136,6 +144,8 @@ export interface AgentFileEntry {
|
||||
project?: string;
|
||||
defaultParams?: Record<string, unknown>;
|
||||
extras?: Record<string, unknown>;
|
||||
/** v7: see LlmProviderFileEntry.visibility — same default ('private'). */
|
||||
visibility?: 'public' | 'private';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -233,6 +233,10 @@ async function maybeStartVirtualLlmRegistrar(
|
||||
if (entry.wake !== undefined) item.wake = entry.wake;
|
||||
if (entry.poolName !== undefined) item.poolName = entry.poolName;
|
||||
if (wireName !== provider.name) item.publishName = wireName;
|
||||
// v7: pass visibility through; registrar already defaults to
|
||||
// 'private' when omitted, and the per-provider override flows
|
||||
// straight through to the register payload.
|
||||
if (entry.visibility !== undefined) item.visibility = entry.visibility;
|
||||
published.push(item);
|
||||
}
|
||||
// v3: forward locally-declared agents alongside the providers. We
|
||||
@@ -255,6 +259,7 @@ async function maybeStartVirtualLlmRegistrar(
|
||||
if (a.project !== undefined) item.project = a.project;
|
||||
if (a.defaultParams !== undefined) item.defaultParams = a.defaultParams;
|
||||
if (a.extras !== undefined) item.extras = a.extras;
|
||||
if (a.visibility !== undefined) item.visibility = a.visibility;
|
||||
publishedAgents.push(item);
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +72,14 @@ export interface RegistrarPublishedProvider {
|
||||
* `publishName ?? provider.name` everywhere.
|
||||
*/
|
||||
publishName?: string;
|
||||
/**
|
||||
* v7: per-user RBAC scoping. mcplocal-published virtuals default to
|
||||
* 'private' (visible only to the publishing user) — workstations
|
||||
* shouldn't broadcast their models org-wide unless explicitly
|
||||
* shared. The publisher can override per provider with
|
||||
* `"visibility": "public"` in their mcplocal config.
|
||||
*/
|
||||
visibility?: 'public' | 'private';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,6 +96,8 @@ export interface RegistrarPublishedAgent {
|
||||
project?: string;
|
||||
defaultParams?: Record<string, unknown>;
|
||||
extras?: Record<string, unknown>;
|
||||
/** v7: per-user RBAC scoping, defaults to 'private' on register. */
|
||||
visibility?: 'public' | 'private';
|
||||
}
|
||||
|
||||
export interface RegistrarOptions {
|
||||
@@ -207,6 +217,10 @@ export class VirtualLlmRegistrar {
|
||||
...(p.tier !== undefined ? { tier: p.tier } : {}),
|
||||
...(p.description !== undefined ? { description: p.description } : {}),
|
||||
...(p.poolName !== undefined ? { poolName: p.poolName } : {}),
|
||||
// v7: virtuals default to private. Operators who want their
|
||||
// workstation model org-visible set "visibility": "public" per
|
||||
// provider in mcplocal config.
|
||||
visibility: p.visibility ?? 'private',
|
||||
initialStatus,
|
||||
};
|
||||
}));
|
||||
@@ -224,6 +238,9 @@ export class VirtualLlmRegistrar {
|
||||
...(a.project !== undefined ? { project: a.project } : {}),
|
||||
...(a.defaultParams !== undefined ? { defaultParams: a.defaultParams } : {}),
|
||||
...(a.extras !== undefined ? { extras: a.extras } : {}),
|
||||
// v7: forward visibility to mcpd. Defaults to 'private' for
|
||||
// virtual agents on the server side when omitted.
|
||||
visibility: a.visibility ?? 'private',
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user