From 366483853ef96d081f14338a154c83d5cc0e7520 Mon Sep 17 00:00:00 2001 From: shishu314 Date: Tue, 26 Aug 2025 17:03:11 -0400 Subject: [PATCH] feat(cli) - Define shared interface for storage (#7049) Co-authored-by: Shi Shu --- packages/core/src/index.ts | 6 +-- packages/core/src/mcp/oauth-provider.test.ts | 4 +- packages/core/src/mcp/oauth-provider.ts | 8 ++-- .../core/src/mcp/oauth-token-storage.test.ts | 9 ++--- packages/core/src/mcp/oauth-token-storage.ts | 40 ++++--------------- packages/core/src/mcp/token-storage/types.ts | 37 +++++++++++++++++ 6 files changed, 57 insertions(+), 47 deletions(-) create mode 100644 packages/core/src/mcp/token-storage/types.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index eb4b9b52..64732c21 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -90,9 +90,9 @@ export * from './tools/mcp-tool.js'; // MCP OAuth export { MCPOAuthProvider } from './mcp/oauth-provider.js'; export type { - MCPOAuthToken, - MCPOAuthCredentials, -} from './mcp/oauth-token-storage.js'; + OAuthToken, + OAuthCredentials, +} from './mcp/token-storage/types.js'; export { MCPOAuthTokenStorage } from './mcp/oauth-token-storage.js'; export type { MCPOAuthConfig } from './mcp/oauth-provider.js'; export type { diff --git a/packages/core/src/mcp/oauth-provider.test.ts b/packages/core/src/mcp/oauth-provider.test.ts index 0ca099de..c13d290c 100644 --- a/packages/core/src/mcp/oauth-provider.test.ts +++ b/packages/core/src/mcp/oauth-provider.test.ts @@ -23,7 +23,7 @@ import type { OAuthClientRegistrationResponse, } from './oauth-provider.js'; import { MCPOAuthProvider } from './oauth-provider.js'; -import type { MCPOAuthToken } from './oauth-token-storage.js'; +import type { OAuthToken } from './token-storage/types.js'; import { MCPOAuthTokenStorage } from './oauth-token-storage.js'; // Mock fetch globally @@ -101,7 +101,7 @@ describe('MCPOAuthProvider', () => { audiences: ['https://api.example.com'], }; - const mockToken: MCPOAuthToken = { + const mockToken: OAuthToken = { accessToken: 'access_token_123', refreshToken: 'refresh_token_456', tokenType: 'Bearer', diff --git a/packages/core/src/mcp/oauth-provider.ts b/packages/core/src/mcp/oauth-provider.ts index c3cb2011..773d2062 100644 --- a/packages/core/src/mcp/oauth-provider.ts +++ b/packages/core/src/mcp/oauth-provider.ts @@ -8,7 +8,7 @@ import * as http from 'node:http'; import * as crypto from 'node:crypto'; import { URL } from 'node:url'; import { openBrowserSecurely } from '../utils/secure-browser-launcher.js'; -import type { MCPOAuthToken } from './oauth-token-storage.js'; +import type { OAuthToken } from './token-storage/types.js'; import { MCPOAuthTokenStorage } from './oauth-token-storage.js'; import { getErrorMessage } from '../utils/errors.js'; import { OAuthUtils } from './oauth-utils.js'; @@ -583,7 +583,7 @@ export class MCPOAuthProvider { serverName: string, config: MCPOAuthConfig, mcpServerUrl?: string, - ): Promise { + ): Promise { // If no authorization URL is provided, try to discover OAuth configuration if (!config.authorizationUrl && mcpServerUrl) { console.log( @@ -777,7 +777,7 @@ export class MCPOAuthProvider { throw new Error('No access token received from token endpoint'); } - const token: MCPOAuthToken = { + const token: OAuthToken = { accessToken: tokenResponse.access_token, tokenType: tokenResponse.token_type || 'Bearer', refreshToken: tokenResponse.refresh_token, @@ -863,7 +863,7 @@ export class MCPOAuthProvider { ); // Update stored token - const newToken: MCPOAuthToken = { + const newToken: OAuthToken = { accessToken: newTokenResponse.access_token, tokenType: newTokenResponse.token_type, refreshToken: newTokenResponse.refresh_token || token.refreshToken, diff --git a/packages/core/src/mcp/oauth-token-storage.test.ts b/packages/core/src/mcp/oauth-token-storage.test.ts index bf8ca2e6..b574c36b 100644 --- a/packages/core/src/mcp/oauth-token-storage.test.ts +++ b/packages/core/src/mcp/oauth-token-storage.test.ts @@ -7,11 +7,8 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { promises as fs } from 'node:fs'; import * as path from 'node:path'; -import type { - MCPOAuthToken, - MCPOAuthCredentials, -} from './oauth-token-storage.js'; import { MCPOAuthTokenStorage } from './oauth-token-storage.js'; +import type { OAuthToken, OAuthCredentials } from './token-storage/types.js'; // Mock file system operations vi.mock('node:fs', () => ({ @@ -29,7 +26,7 @@ vi.mock('node:os', () => ({ })); describe('MCPOAuthTokenStorage', () => { - const mockToken: MCPOAuthToken = { + const mockToken: OAuthToken = { accessToken: 'access_token_123', refreshToken: 'refresh_token_456', tokenType: 'Bearer', @@ -37,7 +34,7 @@ describe('MCPOAuthTokenStorage', () => { expiresAt: Date.now() + 3600000, // 1 hour from now }; - const mockCredentials: MCPOAuthCredentials = { + const mockCredentials: OAuthCredentials = { serverName: 'test-server', token: mockToken, clientId: 'test-client-id', diff --git a/packages/core/src/mcp/oauth-token-storage.ts b/packages/core/src/mcp/oauth-token-storage.ts index 00814ba9..c2c27a27 100644 --- a/packages/core/src/mcp/oauth-token-storage.ts +++ b/packages/core/src/mcp/oauth-token-storage.ts @@ -8,29 +8,7 @@ import { promises as fs } from 'node:fs'; import * as path from 'node:path'; import { Storage } from '../config/storage.js'; import { getErrorMessage } from '../utils/errors.js'; - -/** - * Interface for MCP OAuth tokens. - */ -export interface MCPOAuthToken { - accessToken: string; - refreshToken?: string; - expiresAt?: number; - tokenType: string; - scope?: string; -} - -/** - * Interface for stored MCP OAuth credentials. - */ -export interface MCPOAuthCredentials { - serverName: string; - token: MCPOAuthToken; - clientId?: string; - tokenUrl?: string; - mcpServerUrl?: string; - updatedAt: number; -} +import type { OAuthToken, OAuthCredentials } from './token-storage/types.js'; /** * Class for managing MCP OAuth token storage and retrieval. @@ -58,13 +36,13 @@ export class MCPOAuthTokenStorage { * * @returns A map of server names to credentials */ - static async loadTokens(): Promise> { - const tokenMap = new Map(); + static async loadTokens(): Promise> { + const tokenMap = new Map(); try { const tokenFile = this.getTokenFilePath(); const data = await fs.readFile(tokenFile, 'utf-8'); - const tokens = JSON.parse(data) as MCPOAuthCredentials[]; + const tokens = JSON.parse(data) as OAuthCredentials[]; for (const credential of tokens) { tokenMap.set(credential.serverName, credential); @@ -92,7 +70,7 @@ export class MCPOAuthTokenStorage { */ static async saveToken( serverName: string, - token: MCPOAuthToken, + token: OAuthToken, clientId?: string, tokenUrl?: string, mcpServerUrl?: string, @@ -101,7 +79,7 @@ export class MCPOAuthTokenStorage { const tokens = await this.loadTokens(); - const credential: MCPOAuthCredentials = { + const credential: OAuthCredentials = { serverName, token, clientId, @@ -135,9 +113,7 @@ export class MCPOAuthTokenStorage { * @param serverName The name of the MCP server * @returns The stored credentials or null if not found */ - static async getToken( - serverName: string, - ): Promise { + static async getToken(serverName: string): Promise { const tokens = await this.loadTokens(); return tokens.get(serverName) || null; } @@ -177,7 +153,7 @@ export class MCPOAuthTokenStorage { * @param token The token to check * @returns True if the token is expired */ - static isTokenExpired(token: MCPOAuthToken): boolean { + static isTokenExpired(token: OAuthToken): boolean { if (!token.expiresAt) { return false; // No expiry, assume valid } diff --git a/packages/core/src/mcp/token-storage/types.ts b/packages/core/src/mcp/token-storage/types.ts new file mode 100644 index 00000000..4b7bca66 --- /dev/null +++ b/packages/core/src/mcp/token-storage/types.ts @@ -0,0 +1,37 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * Interface for OAuth tokens. + */ +export interface OAuthToken { + accessToken: string; + refreshToken?: string; + expiresAt?: number; + tokenType: string; + scope?: string; +} + +/** + * Interface for stored OAuth credentials. + */ +export interface OAuthCredentials { + serverName: string; + token: OAuthToken; + clientId?: string; + tokenUrl?: string; + mcpServerUrl?: string; + updatedAt: number; +} + +export interface TokenStorage { + getCredentials(serverName: string): Promise; + setCredentials(credentials: OAuthCredentials): Promise; + deleteCredentials(serverName: string): Promise; + listServers(): Promise; + getAllCredentials(): Promise>; + clearAll(): Promise; +}