summaryrefslogtreecommitdiff
path: root/packages/core/src/mcp
diff options
context:
space:
mode:
Diffstat (limited to 'packages/core/src/mcp')
-rw-r--r--packages/core/src/mcp/google-auth-provider.test.ts67
-rw-r--r--packages/core/src/mcp/google-auth-provider.ts83
2 files changed, 150 insertions, 0 deletions
diff --git a/packages/core/src/mcp/google-auth-provider.test.ts b/packages/core/src/mcp/google-auth-provider.test.ts
new file mode 100644
index 00000000..f481b9e2
--- /dev/null
+++ b/packages/core/src/mcp/google-auth-provider.test.ts
@@ -0,0 +1,67 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { GoogleAuth } from 'google-auth-library';
+import { GoogleCredentialProvider } from './google-auth-provider.js';
+import { vi, describe, beforeEach, it, expect, Mock } from 'vitest';
+import { MCPServerConfig } from '../config/config.js';
+
+vi.mock('google-auth-library');
+
+describe('GoogleCredentialProvider', () => {
+ it('should throw an error if no scopes are provided', () => {
+ expect(() => new GoogleCredentialProvider()).toThrow(
+ 'Scopes must be provided in the oauth config for Google Credentials provider',
+ );
+ });
+
+ it('should use scopes from the config if provided', () => {
+ const config = {
+ oauth: {
+ scopes: ['scope1', 'scope2'],
+ },
+ } as MCPServerConfig;
+ new GoogleCredentialProvider(config);
+ expect(GoogleAuth).toHaveBeenCalledWith({
+ scopes: ['scope1', 'scope2'],
+ });
+ });
+
+ describe('with provider instance', () => {
+ let provider: GoogleCredentialProvider;
+
+ beforeEach(() => {
+ const config = {
+ oauth: {
+ scopes: ['scope1', 'scope2'],
+ },
+ } as MCPServerConfig;
+ provider = new GoogleCredentialProvider(config);
+ vi.clearAllMocks();
+ });
+
+ it('should return credentials', async () => {
+ const mockClient = {
+ getAccessToken: vi.fn().mockResolvedValue({ token: 'test-token' }),
+ };
+ (GoogleAuth.prototype.getClient as Mock).mockResolvedValue(mockClient);
+
+ const credentials = await provider.tokens();
+
+ expect(credentials?.access_token).toBe('test-token');
+ });
+
+ it('should return undefined if access token is not available', async () => {
+ const mockClient = {
+ getAccessToken: vi.fn().mockResolvedValue({ token: null }),
+ };
+ (GoogleAuth.prototype.getClient as Mock).mockResolvedValue(mockClient);
+
+ const credentials = await provider.tokens();
+ expect(credentials).toBeUndefined();
+ });
+ });
+});
diff --git a/packages/core/src/mcp/google-auth-provider.ts b/packages/core/src/mcp/google-auth-provider.ts
new file mode 100644
index 00000000..88cd086b
--- /dev/null
+++ b/packages/core/src/mcp/google-auth-provider.ts
@@ -0,0 +1,83 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js';
+import {
+ OAuthClientInformation,
+ OAuthClientInformationFull,
+ OAuthClientMetadata,
+ OAuthTokens,
+} from '@modelcontextprotocol/sdk/shared/auth.js';
+import { GoogleAuth } from 'google-auth-library';
+import { MCPServerConfig } from '../config/config.js';
+
+export class GoogleCredentialProvider implements OAuthClientProvider {
+ private readonly auth: GoogleAuth;
+
+ // Properties required by OAuthClientProvider, with no-op values
+ readonly redirectUrl = '';
+ readonly clientMetadata: OAuthClientMetadata = {
+ client_name: 'Gemini CLI (Google ADC)',
+ redirect_uris: [],
+ grant_types: [],
+ response_types: [],
+ token_endpoint_auth_method: 'none',
+ };
+ private _clientInformation?: OAuthClientInformationFull;
+
+ constructor(private readonly config?: MCPServerConfig) {
+ const scopes = this.config?.oauth?.scopes;
+ if (!scopes || scopes.length === 0) {
+ throw new Error(
+ 'Scopes must be provided in the oauth config for Google Credentials provider',
+ );
+ }
+ this.auth = new GoogleAuth({
+ scopes,
+ });
+ }
+
+ clientInformation(): OAuthClientInformation | undefined {
+ return this._clientInformation;
+ }
+
+ saveClientInformation(clientInformation: OAuthClientInformationFull): void {
+ this._clientInformation = clientInformation;
+ }
+
+ async tokens(): Promise<OAuthTokens | undefined> {
+ const client = await this.auth.getClient();
+ const accessTokenResponse = await client.getAccessToken();
+
+ if (!accessTokenResponse.token) {
+ console.error('Failed to get access token from Google ADC');
+ return undefined;
+ }
+
+ const tokens: OAuthTokens = {
+ access_token: accessTokenResponse.token,
+ token_type: 'Bearer',
+ };
+ return tokens;
+ }
+
+ saveTokens(_tokens: OAuthTokens): void {
+ // No-op, ADC manages tokens.
+ }
+
+ redirectToAuthorization(_authorizationUrl: URL): void {
+ // No-op
+ }
+
+ saveCodeVerifier(_codeVerifier: string): void {
+ // No-op
+ }
+
+ codeVerifier(): string {
+ // No-op
+ return '';
+ }
+}