feat: McpToken — HTTP-mode mcplocal, CLI verbs, audit plumbing #50
@@ -62,21 +62,31 @@ export function registerProjectMcpEndpoint(app: FastifyInstance, mcpdClient: Mcp
|
|||||||
return existing.router;
|
return existing.router;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTP-mode mcplocal has no pod-level credentials — the default
|
||||||
|
// `mcpdClient.token` is an empty string. Every downstream call from this
|
||||||
|
// request (upstream discovery, LLM config fetch, prompt index for
|
||||||
|
// begin_session) has to use the CALLER's McpToken as the bearer, or mcpd
|
||||||
|
// rejects with 401. Build one per-request client here and thread it
|
||||||
|
// everywhere instead of sprinkling `.withToken(authToken)` at each call site.
|
||||||
|
const requestClient = authToken ? mcpdClient.withToken(authToken) : mcpdClient;
|
||||||
|
|
||||||
// Create new router or refresh existing one
|
// Create new router or refresh existing one
|
||||||
const router = existing?.router ?? new McpRouter();
|
const router = existing?.router ?? new McpRouter();
|
||||||
await refreshProjectUpstreams(router, mcpdClient, projectName, authToken);
|
await refreshProjectUpstreams(router, mcpdClient, projectName, authToken);
|
||||||
|
|
||||||
// Resolve project LLM model: local override → mcpd recommendation → global default
|
// Resolve project LLM model: local override → mcpd recommendation → global default
|
||||||
const localOverride = loadProjectLlmOverride(projectName);
|
const localOverride = loadProjectLlmOverride(projectName);
|
||||||
const mcpdConfig = await fetchProjectLlmConfig(mcpdClient, projectName);
|
const mcpdConfig = await fetchProjectLlmConfig(requestClient, projectName);
|
||||||
const resolvedModel = localOverride?.model ?? mcpdConfig.llmModel ?? undefined;
|
const resolvedModel = localOverride?.model ?? mcpdConfig.llmModel ?? undefined;
|
||||||
|
|
||||||
// If project llmProvider is "none", disable LLM for this project
|
// If project llmProvider is "none", disable LLM for this project
|
||||||
const llmDisabled = mcpdConfig.llmProvider === 'none' || localOverride?.provider === 'none';
|
const llmDisabled = mcpdConfig.llmProvider === 'none' || localOverride?.provider === 'none';
|
||||||
const effectiveRegistry = llmDisabled ? null : (providerRegistry ?? null);
|
const effectiveRegistry = llmDisabled ? null : (providerRegistry ?? null);
|
||||||
|
|
||||||
// Configure prompt resources with SA-scoped client for RBAC
|
// Configure prompt resources with SA-scoped client for RBAC.
|
||||||
const saClient = mcpdClient.withHeaders({ 'X-Service-Account': `project:${projectName}` });
|
// Keep the X-Service-Account header for mcpd-side audit tagging, but carry
|
||||||
|
// the caller's bearer so auth passes (the principal resolves as McpToken:<sha>).
|
||||||
|
const saClient = requestClient.withHeaders({ 'X-Service-Account': `project:${projectName}` });
|
||||||
router.setPromptConfig(saClient, projectName);
|
router.setPromptConfig(saClient, projectName);
|
||||||
|
|
||||||
// System prompt fetcher for LLM consumers (uses router's cached fetcher)
|
// System prompt fetcher for LLM consumers (uses router's cached fetcher)
|
||||||
|
|||||||
@@ -30,9 +30,13 @@ function mockMcpdClient() {
|
|||||||
delete: vi.fn(),
|
delete: vi.fn(),
|
||||||
forward: vi.fn(async () => ({ status: 200, body: [] })),
|
forward: vi.fn(async () => ({ status: 200, body: [] })),
|
||||||
withHeaders: vi.fn(),
|
withHeaders: vi.fn(),
|
||||||
|
withToken: vi.fn(),
|
||||||
|
withTimeout: vi.fn(),
|
||||||
};
|
};
|
||||||
// withHeaders returns a new client-like object (returns self for simplicity)
|
// Chainable withX returns the same client for simplicity
|
||||||
(client.withHeaders as ReturnType<typeof vi.fn>).mockReturnValue(client);
|
(client.withHeaders as ReturnType<typeof vi.fn>).mockReturnValue(client);
|
||||||
|
(client.withToken as ReturnType<typeof vi.fn>).mockReturnValue(client);
|
||||||
|
(client.withTimeout as ReturnType<typeof vi.fn>).mockReturnValue(client);
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user