Path: blob/main/src/vs/workbench/services/authentication/browser/authenticationMcpUsageService.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Queue } from '../../../../base/common/async.js';6import { Disposable } from '../../../../base/common/lifecycle.js';7import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';8import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';9import { ILogService } from '../../../../platform/log/common/log.js';10import { IProductService } from '../../../../platform/product/common/productService.js';11import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';12import { IAuthenticationService } from '../common/authentication.js';1314export interface IAuthenticationMcpUsage {15mcpServerId: string;16mcpServerName: string;17lastUsed: number;18scopes?: string[];19}2021export const IAuthenticationMcpUsageService = createDecorator<IAuthenticationMcpUsageService>('IAuthenticationMcpUsageService');22export interface IAuthenticationMcpUsageService {23readonly _serviceBrand: undefined;24/**25* Initializes the cache of MCP servers that use authentication. Ideally used in a contribution that can be run eventually after the workspace is loaded.26*/27initializeUsageCache(): Promise<void>;28/**29* Checks if an MCP server uses authentication30* @param mcpServerId The id of the MCP server to check31*/32hasUsedAuth(mcpServerId: string): Promise<boolean>;33/**34* Reads the usages for an account35* @param providerId The id of the authentication provider to get usages for36* @param accountName The name of the account to get usages for37*/38readAccountUsages(providerId: string, accountName: string,): IAuthenticationMcpUsage[];39/**40*41* @param providerId The id of the authentication provider to get usages for42* @param accountName The name of the account to get usages for43*/44removeAccountUsage(providerId: string, accountName: string): void;45/**46* Adds a usage for an account47* @param providerId The id of the authentication provider to get usages for48* @param accountName The name of the account to get usages for49* @param mcpServerId The id of the MCP server to add a usage for50* @param mcpServerName The name of the MCP server to add a usage for51*/52addAccountUsage(providerId: string, accountName: string, scopes: ReadonlyArray<string>, mcpServerId: string, mcpServerName: string): void;53}5455export class AuthenticationMcpUsageService extends Disposable implements IAuthenticationMcpUsageService {56_serviceBrand: undefined;5758private _queue = new Queue();59private _mcpServersUsingAuth = new Set<string>();6061constructor(62@IStorageService private readonly _storageService: IStorageService,63@IAuthenticationService private readonly _authenticationService: IAuthenticationService,64@ILogService private readonly _logService: ILogService,65@IProductService productService: IProductService,66) {67super();6869// If an MCP server is listed in `trustedMcpAuthAccess` we should consider it as using auth70const trustedMcpAuthAccess = productService.trustedMcpAuthAccess;71if (Array.isArray(trustedMcpAuthAccess)) {72for (const mcpServerId of trustedMcpAuthAccess) {73this._mcpServersUsingAuth.add(mcpServerId);74}75} else if (trustedMcpAuthAccess) {76for (const mcpServers of Object.values(trustedMcpAuthAccess)) {77for (const mcpServerId of mcpServers) {78this._mcpServersUsingAuth.add(mcpServerId);79}80}81}8283this._register(this._authenticationService.onDidRegisterAuthenticationProvider(84provider => this._queue.queue(85() => this._addToCache(provider.id)86)87));88}8990async initializeUsageCache(): Promise<void> {91await this._queue.queue(() => Promise.all(this._authenticationService.getProviderIds().map(providerId => this._addToCache(providerId))));92}9394async hasUsedAuth(mcpServerId: string): Promise<boolean> {95await this._queue.whenIdle();96return this._mcpServersUsingAuth.has(mcpServerId);97}9899readAccountUsages(providerId: string, accountName: string): IAuthenticationMcpUsage[] {100const accountKey = `${providerId}-${accountName}-mcpserver-usages`;101const storedUsages = this._storageService.get(accountKey, StorageScope.APPLICATION);102let usages: IAuthenticationMcpUsage[] = [];103if (storedUsages) {104try {105usages = JSON.parse(storedUsages);106} catch (e) {107// ignore108}109}110111return usages;112}113114removeAccountUsage(providerId: string, accountName: string): void {115const accountKey = `${providerId}-${accountName}-mcpserver-usages`;116this._storageService.remove(accountKey, StorageScope.APPLICATION);117}118119addAccountUsage(providerId: string, accountName: string, scopes: string[], mcpServerId: string, mcpServerName: string): void {120const accountKey = `${providerId}-${accountName}-mcpserver-usages`;121const usages = this.readAccountUsages(providerId, accountName);122123const existingUsageIndex = usages.findIndex(usage => usage.mcpServerId === mcpServerId);124if (existingUsageIndex > -1) {125usages.splice(existingUsageIndex, 1, {126mcpServerId,127mcpServerName,128scopes,129lastUsed: Date.now()130});131} else {132usages.push({133mcpServerId,134mcpServerName,135scopes,136lastUsed: Date.now()137});138}139140this._storageService.store(accountKey, JSON.stringify(usages), StorageScope.APPLICATION, StorageTarget.MACHINE);141this._mcpServersUsingAuth.add(mcpServerId);142}143144private async _addToCache(providerId: string) {145try {146const accounts = await this._authenticationService.getAccounts(providerId);147for (const account of accounts) {148const usage = this.readAccountUsages(providerId, account.label);149for (const u of usage) {150this._mcpServersUsingAuth.add(u.mcpServerId);151}152}153} catch (e) {154this._logService.error(e);155}156}157}158159registerSingleton(IAuthenticationMcpUsageService, AuthenticationMcpUsageService, InstantiationType.Delayed);160161162