From 7c85bbe28c4efdc7e316276d6cdbff03e4ecc00d Mon Sep 17 00:00:00 2001 From: "Rocco, Marco" <7020500+ech0s7r@users.noreply.github.com> Date: Wed, 6 May 2026 22:08:04 -0400 Subject: [PATCH 1/3] Add injectable DevelopersApi support and OAuth 2.0 tool --- README.md | 1 + modelcontextprotocol/README.md | 4 +- typescript/package-lock.json | 2 +- typescript/package.json | 2 +- typescript/src/modelcontextprotocol/index.ts | 3 + .../src/shared/api/__tests__/index.test.ts | 8 +- typescript/src/shared/api/index.ts | 5 +- .../__tests__/getDocumentation.test.ts | 10 +-- .../__tests__/getDocumentationPage.test.ts | 8 +- .../__tests__/getDocumentationSection.test.ts | 21 ++--- .../__tests__/getOAuth10aGuide.test.ts | 11 ++- .../__tests__/getOAuth20Guide.test.ts | 90 +++++++++++++++++++ .../__tests__/getOpenFinanceGuide.test.ts | 8 +- .../tools/documentation/getDocumentation.ts | 13 +-- .../documentation/getDocumentationPage.ts | 14 +-- .../documentation/getDocumentationSection.ts | 16 ++-- .../tools/documentation/getOAuth10aGuide.ts | 13 +-- .../tools/documentation/getOAuth20Guide.ts | 72 +++++++++++++++ .../documentation/getOpenFinanceGuide.ts | 13 +-- typescript/src/shared/tools/index.ts | 26 +++--- .../__tests__/getApiOperationDetails.test.ts | 20 ++--- .../__tests__/getApiOperationList.test.ts | 14 ++- .../operations/getApiOperationDetails.ts | 30 ++++--- .../tools/operations/getApiOperationList.ts | 16 ++-- .../__tests__/getServicesList.test.ts | 8 +- .../shared/tools/services/getServicesList.ts | 15 ++-- typescript/src/shared/types.ts | 16 ++++ typescript/src/tests/mockDevelopersApi.ts | 10 +++ 28 files changed, 346 insertions(+), 123 deletions(-) create mode 100644 typescript/src/shared/tools/documentation/__tests__/getOAuth20Guide.test.ts create mode 100644 typescript/src/shared/tools/documentation/getOAuth20Guide.ts create mode 100644 typescript/src/tests/mockDevelopersApi.ts diff --git a/README.md b/README.md index b476b7d..1626bac 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ The toolkit provides the following tools for agents to use: * `get-documentation-section-content`: Retrieves the complete content for a specific documentation section. * `get-documentation-page`: Retrieves the complete content of a specific documentation page. * `get-oauth10a-integration-guide`: Retrieves the comprehensive OAuth 1.0a integration guide. +* `get-oauth20-integration-guide`: Retrieves the comprehensive OAuth 2.0 integration guide. * `get-openfinance-integration-guide`: Retrieves the comprehensive Open Finance integration guide. ### API Operations diff --git a/modelcontextprotocol/README.md b/modelcontextprotocol/README.md index ec22f63..5088cf3 100644 --- a/modelcontextprotocol/README.md +++ b/modelcontextprotocol/README.md @@ -14,7 +14,7 @@ This MCP server acts as a bridge between MCP clients and Mastercard Developers r - List available Mastercard services - Query API specifications and their operations - Access documentation for each service -- Retrieve integration guides for OAuth 1.0a and Open Finance +- Retrieve integration guides for OAuth 1.0a, OAuth 2.0, and Open Finance ## Available Tools @@ -40,6 +40,8 @@ This MCP server acts as a bridge between MCP clients and Mastercard Developers r - **`get-oauth10a-integration-guide`**: Retrieves the comprehensive OAuth 1.0a integration guide including step-by-step instructions, code examples, and best practices for Mastercard APIs. +- **`get-oauth20-integration-guide`**: Retrieves the comprehensive OAuth 2.0 integration guide including step-by-step instructions, code examples, and best practices for Mastercard APIs. + - **`get-openfinance-integration-guide`**: Retrieves the comprehensive Open Finance integration guide including setup instructions, API usage examples, and implementation best practices. ## Configuration Options diff --git a/typescript/package-lock.json b/typescript/package-lock.json index 3d7c9e1..6e56215 100644 --- a/typescript/package-lock.json +++ b/typescript/package-lock.json @@ -1,6 +1,6 @@ { "name": "@mastercard/developers-agent-toolkit", - "version": "0.1.4", + "version": "0.1.5", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/typescript/package.json b/typescript/package.json index 59cd184..5c5c0b7 100644 --- a/typescript/package.json +++ b/typescript/package.json @@ -1,5 +1,5 @@ { - "version": "0.1.4", + "version": "0.1.5", "name": "@mastercard/developers-agent-toolkit", "homepage": "https://github.com/mastercard/developers-agent-toolkit", "description": "Agent Toolkit for Mastercard Developers Platform", diff --git a/typescript/src/modelcontextprotocol/index.ts b/typescript/src/modelcontextprotocol/index.ts index 024e6ef..b0f1b3b 100644 --- a/typescript/src/modelcontextprotocol/index.ts +++ b/typescript/src/modelcontextprotocol/index.ts @@ -3,6 +3,9 @@ import { tools } from '@/shared/tools'; import { ToolContext } from '@/shared/types'; import { version } from '../../package.json'; +export type { DevelopersApi, Tool, ToolContext } from '@/shared/types'; +export { tools } from '@/shared/tools'; + export interface MastercardDevelopersAgentToolkitConfig { service?: string; apiSpecification?: string; diff --git a/typescript/src/shared/api/__tests__/index.test.ts b/typescript/src/shared/api/__tests__/index.test.ts index 08fb8bf..93f02dc 100644 --- a/typescript/src/shared/api/__tests__/index.test.ts +++ b/typescript/src/shared/api/__tests__/index.test.ts @@ -1,4 +1,4 @@ -import { MastercardAPIClient } from '@/shared/api'; +import { defaultDevelopersApi, MastercardAPIClient } from '@/shared/api'; import fetch, { RequestInfo, Response } from 'node-fetch'; const mcd = (path: string) => { @@ -8,6 +8,12 @@ const mcd = (path: string) => { const mockFetch = fetch as jest.MockedFunction; jest.mock('node-fetch'); +describe('defaultDevelopersApi', () => { + it('uses the default MastercardAPIClient implementation', () => { + expect(defaultDevelopersApi).toBeInstanceOf(MastercardAPIClient); + }); +}); + describe('MastercardAPIClient', () => { let client: MastercardAPIClient; diff --git a/typescript/src/shared/api/index.ts b/typescript/src/shared/api/index.ts index 7e56035..4b39da7 100644 --- a/typescript/src/shared/api/index.ts +++ b/typescript/src/shared/api/index.ts @@ -1,6 +1,8 @@ import { z } from 'zod'; import fetch from 'node-fetch'; +import type { DevelopersApi } from '@/shared/types'; + const PathSchema = z .string() .min(1, 'Path must be a non-empty string') @@ -106,5 +108,4 @@ export class MastercardAPIClient { } } -const api = new MastercardAPIClient(); -export default api; +export const defaultDevelopersApi: DevelopersApi = new MastercardAPIClient(); diff --git a/typescript/src/shared/tools/documentation/__tests__/getDocumentation.test.ts b/typescript/src/shared/tools/documentation/__tests__/getDocumentation.test.ts index 43daa80..57094ce 100644 --- a/typescript/src/shared/tools/documentation/__tests__/getDocumentation.test.ts +++ b/typescript/src/shared/tools/documentation/__tests__/getDocumentation.test.ts @@ -2,11 +2,9 @@ import { execute, getParameters, } from '@/shared/tools/documentation/getDocumentation'; -import api from '@/shared/api'; +import { createMockApi } from '@/tests/mockDevelopersApi'; -jest.mock('@/shared/api'); - -const mockApi = api as jest.Mocked; +const mockApi = createMockApi(); describe('execute', () => { beforeEach(() => { @@ -17,7 +15,7 @@ describe('execute', () => { const mockResult = 'mock documentation'; mockApi.getDocumentation.mockResolvedValue(mockResult); - const result = await execute({}, { serviceId: 'test-service' }); + const result = await execute({}, mockApi, { serviceId: 'test-service' }); expect(mockApi.getDocumentation).toHaveBeenCalledWith('test-service'); expect(result).toBe(mockResult); @@ -27,7 +25,7 @@ describe('execute', () => { const mockResult = 'mock documentation'; mockApi.getDocumentation.mockResolvedValue(mockResult); - const result = await execute({ serviceId: 'context-service' }, {}); + const result = await execute({ serviceId: 'context-service' }, mockApi, {}); expect(mockApi.getDocumentation).toHaveBeenCalledWith('context-service'); expect(result).toBe(mockResult); diff --git a/typescript/src/shared/tools/documentation/__tests__/getDocumentationPage.test.ts b/typescript/src/shared/tools/documentation/__tests__/getDocumentationPage.test.ts index b79b542..1b73cce 100644 --- a/typescript/src/shared/tools/documentation/__tests__/getDocumentationPage.test.ts +++ b/typescript/src/shared/tools/documentation/__tests__/getDocumentationPage.test.ts @@ -2,11 +2,9 @@ import { execute, getParameters, } from '@/shared/tools/documentation/getDocumentationPage'; -import api from '@/shared/api'; +import { createMockApi } from '@/tests/mockDevelopersApi'; -jest.mock('@/shared/api'); - -const mockApi = api as jest.Mocked; +const mockApi = createMockApi(); describe('execute', () => { beforeEach(() => { @@ -17,7 +15,7 @@ describe('execute', () => { const mockResult = 'mock documentation page content'; mockApi.getDocumentationPage.mockResolvedValue(mockResult); - const result = await execute({}, { pagePath: '/test/page.md' }); + const result = await execute({}, mockApi, { pagePath: '/test/page.md' }); expect(mockApi.getDocumentationPage).toHaveBeenCalledWith('/test/page.md'); expect(result).toBe(mockResult); diff --git a/typescript/src/shared/tools/documentation/__tests__/getDocumentationSection.test.ts b/typescript/src/shared/tools/documentation/__tests__/getDocumentationSection.test.ts index d11e988..8cee7d1 100644 --- a/typescript/src/shared/tools/documentation/__tests__/getDocumentationSection.test.ts +++ b/typescript/src/shared/tools/documentation/__tests__/getDocumentationSection.test.ts @@ -2,11 +2,9 @@ import { execute, getParameters, } from '@/shared/tools/documentation/getDocumentationSection'; -import api from '@/shared/api'; +import { createMockApi } from '@/tests/mockDevelopersApi'; -jest.mock('@/shared/api'); - -const mockApi = api as jest.Mocked; +const mockApi = createMockApi(); describe('execute', () => { beforeEach(() => { @@ -17,10 +15,10 @@ describe('execute', () => { const mockResult = 'mock documentation section content'; mockApi.getDocumentationSection.mockResolvedValue(mockResult); - const result = await execute( - {}, - { serviceId: 'test-service', sectionId: 'test-section' } - ); + const result = await execute({}, mockApi, { + serviceId: 'test-service', + sectionId: 'test-section', + }); expect(mockApi.getDocumentationSection).toHaveBeenCalledWith( 'test-service', @@ -33,10 +31,9 @@ describe('execute', () => { const mockResult = 'mock documentation section content'; mockApi.getDocumentationSection.mockResolvedValue(mockResult); - const result = await execute( - { serviceId: 'context-service' }, - { sectionId: 'test-section' } - ); + const result = await execute({ serviceId: 'context-service' }, mockApi, { + sectionId: 'test-section', + }); expect(mockApi.getDocumentationSection).toHaveBeenCalledWith( 'context-service', diff --git a/typescript/src/shared/tools/documentation/__tests__/getOAuth10aGuide.test.ts b/typescript/src/shared/tools/documentation/__tests__/getOAuth10aGuide.test.ts index dc86616..51bc616 100644 --- a/typescript/src/shared/tools/documentation/__tests__/getOAuth10aGuide.test.ts +++ b/typescript/src/shared/tools/documentation/__tests__/getOAuth10aGuide.test.ts @@ -2,14 +2,13 @@ import { execute, getParameters, } from '@/shared/tools/documentation/getOAuth10aGuide'; -import api from '@/shared/api'; +import { createMockApi } from '@/tests/mockDevelopersApi'; import fetch from 'node-fetch'; -jest.mock('@/shared/api'); jest.mock('node-fetch'); -const mockApi = api as jest.Mocked; const mockFetch = fetch as jest.MockedFunction; +const mockApi = createMockApi(); describe('execute', () => { beforeEach(() => { @@ -22,7 +21,7 @@ describe('execute', () => { const mockResult = 'mock OAuth 1.0a guide content'; mockApi.getDocumentationPage.mockResolvedValue(mockResult); - const result = await execute({}, { language: language as any }); + const result = await execute({}, mockApi, { language: language as any }); expect(mockApi.getDocumentationPage).toHaveBeenCalledWith( '/platform/documentation/authentication/using-oauth-1a-to-access-mastercard-apis/index.md' @@ -48,7 +47,7 @@ describe('execute', () => { text: jest.fn().mockResolvedValue(mockGithubContent), } as any); - const result = await execute({}, { language: language as any }); + const result = await execute({}, mockApi, { language: language as any }); expect(mockFetch).toHaveBeenCalledWith( `https://raw.githubusercontent.com/Mastercard/${expectedRepo}/refs/heads/main/README.md` @@ -64,7 +63,7 @@ describe('execute', () => { } as any); mockApi.getDocumentationPage.mockResolvedValue(mockApiResult); - const result = await execute({}, { language: 'java' }); + const result = await execute({}, mockApi, { language: 'java' }); expect(mockFetch).toHaveBeenCalledWith( 'https://raw.githubusercontent.com/Mastercard/oauth1-signer-java/refs/heads/main/README.md' diff --git a/typescript/src/shared/tools/documentation/__tests__/getOAuth20Guide.test.ts b/typescript/src/shared/tools/documentation/__tests__/getOAuth20Guide.test.ts new file mode 100644 index 0000000..b5e4b36 --- /dev/null +++ b/typescript/src/shared/tools/documentation/__tests__/getOAuth20Guide.test.ts @@ -0,0 +1,90 @@ +import { + execute, + getParameters, +} from '@/shared/tools/documentation/getOAuth20Guide'; +import { createMockApi } from '@/tests/mockDevelopersApi'; +import fetch from 'node-fetch'; + +jest.mock('node-fetch'); + +const mockFetch = fetch as jest.MockedFunction; +const mockApi = createMockApi(); + +describe('execute', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it.each([[null], [''], ['others'], ['invalid']])( + 'should get generic OAuth 2.0 integration guide and return it when no or invalid language is specified', + async (language) => { + const mockResult = 'mock OAuth 2.0 guide content'; + mockApi.getDocumentationPage.mockResolvedValue(mockResult); + + const result = await execute({}, mockApi, { language: language as any }); + + expect(mockApi.getDocumentationPage).toHaveBeenCalledWith( + '/platform/documentation/authentication/using-oauth-2-to-access-mastercard-apis/index.md' + ); + expect(result).toBe(mockResult); + } + ); + + it.each([ + ['java', 'oauth2-client-java'], + ['kotlin', 'oauth2-client-java'], + ['javascript', 'oauth2-client-js'], + ['typescript', 'oauth2-client-js'], + ])( + 'should get OAuth 2.0 integration guide with %s language from GitHub', + async (language, expectedRepo) => { + const mockGithubContent = `mock OAuth 2.0 guide content with ${language} examples from GitHub`; + mockFetch.mockResolvedValue({ + ok: true, + text: jest.fn().mockResolvedValue(mockGithubContent), + } as any); + + const result = await execute({}, mockApi, { language: language as any }); + + expect(mockFetch).toHaveBeenCalledWith( + `https://raw.githubusercontent.com/Mastercard/${expectedRepo}/refs/heads/main/README.md` + ); + expect(result).toBe(mockGithubContent); + } + ); + + it('should fallback to generic OAuth 2.0 guide when GitHub fetch fails', async () => { + const mockApiResult = 'mock OAuth 2.0 guide content from API'; + mockFetch.mockResolvedValue({ ok: false } as any); + mockApi.getDocumentationPage.mockResolvedValue(mockApiResult); + + const result = await execute({}, mockApi, { language: 'java' }); + + expect(mockFetch).toHaveBeenCalledWith( + 'https://raw.githubusercontent.com/Mastercard/oauth2-client-java/refs/heads/main/README.md' + ); + expect(mockApi.getDocumentationPage).toHaveBeenCalledWith( + '/platform/documentation/authentication/using-oauth-2-to-access-mastercard-apis/index.md' + ); + expect(result).toBe(mockApiResult); + }); +}); + +describe('getParameters', () => { + it('should return the correct parameters', () => { + const parameters = getParameters({}); + + const fields = Object.keys(parameters.shape); + expect(fields).toEqual(['language']); + expect(fields.length).toBe(1); + expect(parameters.shape.language).toBeDefined(); + expect(parameters.shape.language.isOptional()).toBe(true); + }); + + it('should not accept languages unsupported by OAuth 2.0', () => { + const schema = getParameters({}); + expect(schema.safeParse({ language: 'c#' }).success).toBe(false); + expect(schema.safeParse({ language: 'python' }).success).toBe(false); + expect(schema.safeParse({ language: 'golang' }).success).toBe(false); + }); +}); diff --git a/typescript/src/shared/tools/documentation/__tests__/getOpenFinanceGuide.test.ts b/typescript/src/shared/tools/documentation/__tests__/getOpenFinanceGuide.test.ts index 489b655..8116ad4 100644 --- a/typescript/src/shared/tools/documentation/__tests__/getOpenFinanceGuide.test.ts +++ b/typescript/src/shared/tools/documentation/__tests__/getOpenFinanceGuide.test.ts @@ -2,11 +2,9 @@ import { execute, getParameters, } from '@/shared/tools/documentation/getOpenFinanceGuide'; -import api from '@/shared/api'; +import { createMockApi } from '@/tests/mockDevelopersApi'; -jest.mock('@/shared/api'); - -const mockApi = api as jest.Mocked; +const mockApi = createMockApi(); describe('execute', () => { beforeEach(() => { @@ -17,7 +15,7 @@ describe('execute', () => { const mockResult = 'mock Open Finance guide content'; mockApi.getDocumentationPage.mockResolvedValue(mockResult); - const result = await execute({}, {}); + const result = await execute({}, mockApi, {}); expect(mockApi.getDocumentationPage).toHaveBeenCalledWith( '/open-finance-us/documentation/quick-start-guide/index.md' diff --git a/typescript/src/shared/tools/documentation/getDocumentation.ts b/typescript/src/shared/tools/documentation/getDocumentation.ts index cccda58..f2bc091 100644 --- a/typescript/src/shared/tools/documentation/getDocumentation.ts +++ b/typescript/src/shared/tools/documentation/getDocumentation.ts @@ -1,6 +1,5 @@ import { z } from 'zod'; -import { Tool, ToolContext } from '@/shared/types'; -import api from '@/shared/api'; +import { DevelopersApi, Tool, ToolContext } from '@/shared/types'; const getDescription = (context: ToolContext): string => { const baseDescription = `Provides an overview of all available documentation for a specific Mastercard service @@ -35,13 +34,17 @@ export const getParameters = (context: ToolContext): z.ZodObject => { export const execute = async ( context: ToolContext, + developersApi: DevelopersApi, params: z.infer> ): Promise => { const serviceId = context.serviceId || params.serviceId; - return await api.getDocumentation(serviceId); + return await developersApi.getDocumentation(serviceId); }; -export const getDocumentation = (context: ToolContext): Tool => ({ +export const getDocumentation = ( + context: ToolContext, + developersApi: DevelopersApi +): Tool => ({ name: 'get-documentation', title: 'Get Documentation', description: getDescription(context), @@ -52,5 +55,5 @@ export const getDocumentation = (context: ToolContext): Tool => ({ idempotentHint: true, openWorldHint: true, }, - execute: (params) => execute(context, params), + execute: (params) => execute(context, developersApi, params), }); diff --git a/typescript/src/shared/tools/documentation/getDocumentationPage.ts b/typescript/src/shared/tools/documentation/getDocumentationPage.ts index cb3b500..64e7ca0 100644 --- a/typescript/src/shared/tools/documentation/getDocumentationPage.ts +++ b/typescript/src/shared/tools/documentation/getDocumentationPage.ts @@ -1,6 +1,5 @@ import { z } from 'zod'; -import { Tool, ToolContext } from '@/shared/types'; -import api from '@/shared/api'; +import { DevelopersApi, Tool, ToolContext } from '@/shared/types'; const getDescription = (_context: ToolContext): string => { return `Retrieves the complete content of a specific documentation page. @@ -14,6 +13,7 @@ export const getParameters = (_context: ToolContext): z.ZodObject => { pagePath: z .string() .min(1) + .startsWith('/') .describe( "The full path to the documentation page (e.g., '/send/documentation/use-cases/index.md')" ), @@ -22,12 +22,16 @@ export const getParameters = (_context: ToolContext): z.ZodObject => { export const execute = async ( _context: ToolContext, + developersApi: DevelopersApi, params: z.infer> ): Promise => { - return await api.getDocumentationPage(params.pagePath); + return await developersApi.getDocumentationPage(params.pagePath); }; -export const getDocumentationPage = (context: ToolContext): Tool => ({ +export const getDocumentationPage = ( + context: ToolContext, + developersApi: DevelopersApi +): Tool => ({ name: 'get-documentation-page', title: 'Get Documentation Page', description: getDescription(context), @@ -38,5 +42,5 @@ export const getDocumentationPage = (context: ToolContext): Tool => ({ idempotentHint: true, openWorldHint: true, }, - execute: (params) => execute(context, params), + execute: (params) => execute(context, developersApi, params), }); diff --git a/typescript/src/shared/tools/documentation/getDocumentationSection.ts b/typescript/src/shared/tools/documentation/getDocumentationSection.ts index ee1f518..76f86c1 100644 --- a/typescript/src/shared/tools/documentation/getDocumentationSection.ts +++ b/typescript/src/shared/tools/documentation/getDocumentationSection.ts @@ -1,6 +1,5 @@ import { z } from 'zod'; -import { Tool, ToolContext } from '@/shared/types'; -import api from '@/shared/api'; +import { DevelopersApi, Tool, ToolContext } from '@/shared/types'; const getDescription = (context: ToolContext): string => { const baseDescription = ` @@ -52,13 +51,20 @@ export const getParameters = (context: ToolContext): z.ZodObject => { export const execute = async ( context: ToolContext, + developersApi: DevelopersApi, params: z.infer> ): Promise => { const serviceId = context.serviceId || params.serviceId; - return await api.getDocumentationSection(serviceId, params.sectionId); + return await developersApi.getDocumentationSection( + serviceId, + params.sectionId + ); }; -export const getDocumentationSection = (context: ToolContext): Tool => ({ +export const getDocumentationSection = ( + context: ToolContext, + developersApi: DevelopersApi +): Tool => ({ name: 'get-documentation-section-content', title: 'Get Documentation Section Content', description: getDescription(context), @@ -69,5 +75,5 @@ export const getDocumentationSection = (context: ToolContext): Tool => ({ idempotentHint: true, openWorldHint: true, }, - execute: (params) => execute(context, params), + execute: (params) => execute(context, developersApi, params), }); diff --git a/typescript/src/shared/tools/documentation/getOAuth10aGuide.ts b/typescript/src/shared/tools/documentation/getOAuth10aGuide.ts index 2f625d1..0334668 100644 --- a/typescript/src/shared/tools/documentation/getOAuth10aGuide.ts +++ b/typescript/src/shared/tools/documentation/getOAuth10aGuide.ts @@ -1,7 +1,6 @@ import { z } from 'zod'; import fetch from 'node-fetch'; -import { Tool, ToolContext } from '@/shared/types'; -import api from '@/shared/api'; +import { DevelopersApi, Tool, ToolContext } from '@/shared/types'; const getDescription = (_context: ToolContext): string => { return `Retrieves the comprehensive OAuth 1.0a integration guide including step-by-step instructions, @@ -31,6 +30,7 @@ export const getParameters = (_context: ToolContext): z.ZodObject => { export const execute = async ( _context: ToolContext, + developersApi: DevelopersApi, params: z.infer> ): Promise => { const basePath = @@ -69,10 +69,13 @@ export const execute = async ( } // Fallback to fetching the general OAuth 1.0a guide - return await api.getDocumentationPage(basePath); + return await developersApi.getDocumentationPage(basePath); }; -export const getOAuth10aGuide = (context: ToolContext): Tool => ({ +export const getOAuth10aGuide = ( + context: ToolContext, + developersApi: DevelopersApi +): Tool => ({ name: 'get-oauth10a-integration-guide', title: 'Get OAuth 1.0a Integration Guide', description: getDescription(context), @@ -83,5 +86,5 @@ export const getOAuth10aGuide = (context: ToolContext): Tool => ({ idempotentHint: true, openWorldHint: true, }, - execute: (params) => execute(context, params), + execute: (params) => execute(context, developersApi, params), }); diff --git a/typescript/src/shared/tools/documentation/getOAuth20Guide.ts b/typescript/src/shared/tools/documentation/getOAuth20Guide.ts new file mode 100644 index 0000000..adccf25 --- /dev/null +++ b/typescript/src/shared/tools/documentation/getOAuth20Guide.ts @@ -0,0 +1,72 @@ +import { z } from 'zod'; +import fetch from 'node-fetch'; +import { DevelopersApi, Tool, ToolContext } from '@/shared/types'; + +const getDescription = (_context: ToolContext): string => { + return `Retrieves the comprehensive OAuth 2.0 integration guide including step-by-step instructions, +code examples, and best practices for Mastercard APIs. Optionally specify a programming language +to get language-specific examples and guidance.`; +}; + +export const getParameters = (_context: ToolContext): z.ZodObject => { + return z.object({ + language: z + .enum(['java', 'kotlin', 'javascript', 'typescript', 'others']) + .optional() + .describe( + 'Programming language for language-specific examples and guidance' + ), + }); +}; + +export const execute = async ( + _context: ToolContext, + developersApi: DevelopersApi, + params: z.infer> +): Promise => { + const basePath = + '/platform/documentation/authentication/using-oauth-2-to-access-mastercard-apis/index.md'; + + if (params.language) { + let repositoryName: string | undefined; + switch (params.language) { + case 'java': + case 'kotlin': + repositoryName = 'oauth2-client-java'; + break; + case 'javascript': + case 'typescript': + repositoryName = 'oauth2-client-js'; + break; + } + + if (repositoryName !== undefined) { + const githubUrl = `https://raw.githubusercontent.com/Mastercard/${repositoryName}/refs/heads/main/README.md`; + const response = await fetch(githubUrl); + + if (response.ok) { + return await response.text(); + } + } + } + + // Fallback to fetching the general OAuth 2.0 guide + return await developersApi.getDocumentationPage(basePath); +}; + +export const getOAuth20Guide = ( + context: ToolContext, + developersApi: DevelopersApi +): Tool => ({ + name: 'get-oauth20-integration-guide', + title: 'Get OAuth 2.0 Integration Guide', + description: getDescription(context), + parameters: getParameters(context), + annotations: { + readOnlyHint: true, + destructiveHint: false, + idempotentHint: true, + openWorldHint: true, + }, + execute: (params) => execute(context, developersApi, params), +}); diff --git a/typescript/src/shared/tools/documentation/getOpenFinanceGuide.ts b/typescript/src/shared/tools/documentation/getOpenFinanceGuide.ts index 762d9e0..3632f30 100644 --- a/typescript/src/shared/tools/documentation/getOpenFinanceGuide.ts +++ b/typescript/src/shared/tools/documentation/getOpenFinanceGuide.ts @@ -1,6 +1,5 @@ import { z } from 'zod'; -import { Tool, ToolContext } from '@/shared/types'; -import api from '@/shared/api'; +import { DevelopersApi, Tool, ToolContext } from '@/shared/types'; const getDescription = (_context: ToolContext): string => { return `Retrieves the comprehensive Open Finance (previously known as Open Banking) integration @@ -13,14 +12,18 @@ export const getParameters = (_context: ToolContext): z.ZodObject => { export const execute = async ( _context: ToolContext, + developersApi: DevelopersApi, _params: z.infer> ): Promise => { - return await api.getDocumentationPage( + return await developersApi.getDocumentationPage( '/open-finance-us/documentation/quick-start-guide/index.md' ); }; -export const getOpenFinanceGuide = (context: ToolContext): Tool => ({ +export const getOpenFinanceGuide = ( + context: ToolContext, + developersApi: DevelopersApi +): Tool => ({ name: 'get-openfinance-integration-guide', title: 'Get Open Finance Integration Guide', description: getDescription(context), @@ -31,5 +34,5 @@ export const getOpenFinanceGuide = (context: ToolContext): Tool => ({ idempotentHint: true, openWorldHint: true, }, - execute: (params) => execute(context, params), + execute: (params) => execute(context, developersApi, params), }); diff --git a/typescript/src/shared/tools/index.ts b/typescript/src/shared/tools/index.ts index 3b1c1a9..7c5e85c 100644 --- a/typescript/src/shared/tools/index.ts +++ b/typescript/src/shared/tools/index.ts @@ -1,10 +1,12 @@ -import { Tool, ToolContext } from '@/shared/types'; +import { defaultDevelopersApi } from '@/shared/api'; +import { DevelopersApi, Tool, ToolContext } from '@/shared/types'; // Documentation tools import { getDocumentation } from '@/shared/tools/documentation/getDocumentation'; import { getDocumentationSection } from '@/shared/tools/documentation/getDocumentationSection'; import { getDocumentationPage } from '@/shared/tools/documentation/getDocumentationPage'; import { getOAuth10aGuide } from '@/shared/tools/documentation/getOAuth10aGuide'; +import { getOAuth20Guide } from '@/shared/tools/documentation/getOAuth20Guide'; import { getOpenFinanceGuide } from '@/shared/tools/documentation/getOpenFinanceGuide'; // Services tools @@ -14,18 +16,22 @@ import { getServicesList } from '@/shared/tools/services/getServicesList'; import { getApiOperationList } from '@/shared/tools/operations/getApiOperationList'; import { getApiOperationDetails } from '@/shared/tools/operations/getApiOperationDetails'; -export const tools = (context: ToolContext): Tool[] => [ +export const tools = ( + context: ToolContext = {}, + developersApi: DevelopersApi = defaultDevelopersApi +): Tool[] => [ // Services - getServicesList(context), + getServicesList(context, developersApi), // Documentation - getDocumentation(context), - getDocumentationSection(context), - getDocumentationPage(context), - getOAuth10aGuide(context), - getOpenFinanceGuide(context), + getDocumentation(context, developersApi), + getDocumentationSection(context, developersApi), + getDocumentationPage(context, developersApi), + getOAuth10aGuide(context, developersApi), + getOAuth20Guide(context, developersApi), + getOpenFinanceGuide(context, developersApi), // API Operations - getApiOperationList(context), - getApiOperationDetails(context), + getApiOperationList(context, developersApi), + getApiOperationDetails(context, developersApi), ]; diff --git a/typescript/src/shared/tools/operations/__tests__/getApiOperationDetails.test.ts b/typescript/src/shared/tools/operations/__tests__/getApiOperationDetails.test.ts index f5c0483..3e00488 100644 --- a/typescript/src/shared/tools/operations/__tests__/getApiOperationDetails.test.ts +++ b/typescript/src/shared/tools/operations/__tests__/getApiOperationDetails.test.ts @@ -2,11 +2,9 @@ import { execute, getParameters, } from '@/shared/tools/operations/getApiOperationDetails'; -import api from '@/shared/api'; +import { createMockApi } from '@/tests/mockDevelopersApi'; -jest.mock('@/shared/api'); - -const mockApi = api as jest.Mocked; +const mockApi = createMockApi(); describe('execute', () => { beforeEach(() => { @@ -17,14 +15,11 @@ describe('execute', () => { const mockResult = 'mock operation details'; mockApi.getApiOperationDetails.mockResolvedValue(mockResult); - const result = await execute( - {}, - { - apiSpecificationPath: '/test/path.yaml', - method: 'GET', - path: '/test/endpoint', - } - ); + const result = await execute({}, mockApi, { + apiSpecificationPath: '/test/path.yaml', + method: 'GET', + path: '/test/endpoint', + }); expect(mockApi.getApiOperationDetails).toHaveBeenCalledWith( '/test/path.yaml', @@ -40,6 +35,7 @@ describe('execute', () => { const result = await execute( { apiSpecificationPath: '/context/path.yaml' }, + mockApi, { method: 'POST', path: '/context/endpoint', diff --git a/typescript/src/shared/tools/operations/__tests__/getApiOperationList.test.ts b/typescript/src/shared/tools/operations/__tests__/getApiOperationList.test.ts index c9582c3..21cc1de 100644 --- a/typescript/src/shared/tools/operations/__tests__/getApiOperationList.test.ts +++ b/typescript/src/shared/tools/operations/__tests__/getApiOperationList.test.ts @@ -2,11 +2,9 @@ import { execute, getParameters, } from '@/shared/tools/operations/getApiOperationList'; -import api from '@/shared/api'; +import { createMockApi } from '@/tests/mockDevelopersApi'; -jest.mock('@/shared/api'); - -const mockApi = api as jest.Mocked; +const mockApi = createMockApi(); describe('execute', () => { beforeEach(() => { @@ -17,10 +15,9 @@ describe('execute', () => { const mockResult = 'mock operations list'; mockApi.getApiOperations.mockResolvedValue(mockResult); - const result = await execute( - {}, - { apiSpecificationPath: '/test/path.yaml' } - ); + const result = await execute({}, mockApi, { + apiSpecificationPath: '/test/path.yaml', + }); expect(mockApi.getApiOperations).toHaveBeenCalledWith('/test/path.yaml'); expect(result).toBe(mockResult); @@ -32,6 +29,7 @@ describe('execute', () => { const result = await execute( { apiSpecificationPath: '/context/path.yaml' }, + mockApi, {} ); diff --git a/typescript/src/shared/tools/operations/getApiOperationDetails.ts b/typescript/src/shared/tools/operations/getApiOperationDetails.ts index e02d844..87c8f9e 100644 --- a/typescript/src/shared/tools/operations/getApiOperationDetails.ts +++ b/typescript/src/shared/tools/operations/getApiOperationDetails.ts @@ -1,6 +1,5 @@ import { z } from 'zod'; -import { Tool, ToolContext } from '@/shared/types'; -import api from '@/shared/api'; +import { DevelopersApi, Tool, ToolContext } from '@/shared/types'; const getDescription = (context: ToolContext): string => { const baseDescription = `Provides detailed information about a specific API operation including parameter definitions, @@ -27,15 +26,19 @@ or /open-finance-us/swagger/openbanking-us.yaml) - path (str): The API endpoint path from the specification (e.g., /payments, /accounts/{id})`; }; +const httpMethod = z + .string() + .transform((value) => value.toUpperCase()) + .pipe(z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE'])); + export const getParameters = (context: ToolContext): z.ZodObject => { const baseParams = { - method: z - .string() - .describe( - 'The HTTP method of the operation (e.g., GET, POST, PUT, DELETE)' - ), + method: httpMethod.describe( + 'The HTTP method of the operation (e.g., GET, POST, PUT, DELETE)' + ), path: z .string() + .startsWith('/') .describe( 'The API endpoint path from the specification (e.g., /payments, /accounts/{id})' ), @@ -48,6 +51,7 @@ export const getParameters = (context: ToolContext): z.ZodObject => { return z.object({ apiSpecificationPath: z .string() + .startsWith('/') .describe( 'The path to the API specification (e.g., /open-finance-us/swagger/openbanking-us.yaml)' ), @@ -57,16 +61,17 @@ export const getParameters = (context: ToolContext): z.ZodObject => { export const execute = async ( context: ToolContext, + developersApi: DevelopersApi, params: z.infer> ): Promise => { if (context.apiSpecificationPath) { - return await api.getApiOperationDetails( + return await developersApi.getApiOperationDetails( context.apiSpecificationPath, params.method, params.path ); } else { - return await api.getApiOperationDetails( + return await developersApi.getApiOperationDetails( params.apiSpecificationPath, params.method, params.path @@ -74,7 +79,10 @@ export const execute = async ( } }; -export const getApiOperationDetails = (context: ToolContext): Tool => ({ +export const getApiOperationDetails = ( + context: ToolContext, + developersApi: DevelopersApi +): Tool => ({ name: 'get-api-operation-details', title: 'Get API Operation Details', description: getDescription(context), @@ -85,5 +93,5 @@ export const getApiOperationDetails = (context: ToolContext): Tool => ({ idempotentHint: true, openWorldHint: true, }, - execute: (params) => execute(context, params), + execute: (params) => execute(context, developersApi, params), }); diff --git a/typescript/src/shared/tools/operations/getApiOperationList.ts b/typescript/src/shared/tools/operations/getApiOperationList.ts index 2d2697d..e2b6096 100644 --- a/typescript/src/shared/tools/operations/getApiOperationList.ts +++ b/typescript/src/shared/tools/operations/getApiOperationList.ts @@ -1,6 +1,5 @@ import { z } from 'zod'; -import { Tool, ToolContext } from '@/shared/types'; -import api from '@/shared/api'; +import { DevelopersApi, Tool, ToolContext } from '@/shared/types'; const getDescription = (context: ToolContext): string => { const baseDescription = `Provides a summary of all API operations for a specific Mastercard API @@ -26,6 +25,7 @@ export const getParameters = (context: ToolContext): z.ZodObject => { return z.object({ apiSpecificationPath: z .string() + .startsWith('/') .describe( 'The path to the API specification file (e.g., /open-finance-us/swagger/openbanking-us.yaml)' ), @@ -34,16 +34,20 @@ export const getParameters = (context: ToolContext): z.ZodObject => { export const execute = async ( context: ToolContext, + developersApi: DevelopersApi, params: z.infer> ): Promise => { if (context.apiSpecificationPath) { - return await api.getApiOperations(context.apiSpecificationPath); + return await developersApi.getApiOperations(context.apiSpecificationPath); } else { - return await api.getApiOperations(params.apiSpecificationPath); + return await developersApi.getApiOperations(params.apiSpecificationPath); } }; -export const getApiOperationList = (context: ToolContext): Tool => ({ +export const getApiOperationList = ( + context: ToolContext, + developersApi: DevelopersApi +): Tool => ({ name: 'get-api-operation-list', title: 'Get API Operation List', description: getDescription(context), @@ -54,5 +58,5 @@ export const getApiOperationList = (context: ToolContext): Tool => ({ idempotentHint: true, openWorldHint: true, }, - execute: (params) => execute(context, params), + execute: (params) => execute(context, developersApi, params), }); diff --git a/typescript/src/shared/tools/services/__tests__/getServicesList.test.ts b/typescript/src/shared/tools/services/__tests__/getServicesList.test.ts index 13921d6..f0bf2e8 100644 --- a/typescript/src/shared/tools/services/__tests__/getServicesList.test.ts +++ b/typescript/src/shared/tools/services/__tests__/getServicesList.test.ts @@ -2,11 +2,9 @@ import { execute, getParameters, } from '@/shared/tools/services/getServicesList'; -import api from '@/shared/api'; +import { createMockApi } from '@/tests/mockDevelopersApi'; -jest.mock('@/shared/api'); - -const mockApi = api as jest.Mocked; +const mockApi = createMockApi(); describe('execute', () => { beforeEach(() => { @@ -17,7 +15,7 @@ describe('execute', () => { const mockResult = 'mock services list'; mockApi.listServices.mockResolvedValue(mockResult); - const result = await execute({}, {}); + const result = await execute({}, mockApi, {}); expect(mockApi.listServices).toHaveBeenCalledTimes(1); expect(result).toBe(mockResult); diff --git a/typescript/src/shared/tools/services/getServicesList.ts b/typescript/src/shared/tools/services/getServicesList.ts index 826c4a6..6de3f0a 100644 --- a/typescript/src/shared/tools/services/getServicesList.ts +++ b/typescript/src/shared/tools/services/getServicesList.ts @@ -1,9 +1,8 @@ import { z } from 'zod'; -import { Tool, ToolContext } from '@/shared/types'; -import api from '@/shared/api'; +import { DevelopersApi, Tool, ToolContext } from '@/shared/types'; const getDescription = (_context: ToolContext): string => { - return `Lists all available Mastercard Developers Products and Services with their basic information + return `Lists all available Mastercard Developers Products and Services with their basic information including title, description, and service id. IMPORTANT: The response contains both 'Products' (business offerings) and 'Services' (technical APIs with serviceIds). Use "serviceId" for each service for any tools that require serviceId as the parameter. `; @@ -15,12 +14,16 @@ export const getParameters = (_context: ToolContext): z.ZodObject => { export const execute = async ( _context: ToolContext, + developersApi: DevelopersApi, _params: z.infer> ): Promise => { - return await api.listServices(); + return await developersApi.listServices(); }; -export const getServicesList = (context: ToolContext): Tool => ({ +export const getServicesList = ( + context: ToolContext, + developersApi: DevelopersApi +): Tool => ({ name: 'get-services-list', title: 'Get Services List', description: getDescription(context), @@ -31,5 +34,5 @@ export const getServicesList = (context: ToolContext): Tool => ({ idempotentHint: true, openWorldHint: true, }, - execute: (params) => execute(context, params), + execute: (params) => execute(context, developersApi, params), }); diff --git a/typescript/src/shared/types.ts b/typescript/src/shared/types.ts index ffd65bc..0b72ecc 100644 --- a/typescript/src/shared/types.ts +++ b/typescript/src/shared/types.ts @@ -10,6 +10,22 @@ export interface Tool { execute: (params: any) => Promise; } +export interface DevelopersApi { + listServices(): Promise; + getDocumentation(serviceId: string): Promise; + getDocumentationSection( + serviceId: string, + sectionId: string + ): Promise; + getDocumentationPage(pagePath: string): Promise; + getApiOperations(apiSpecificationPath: string): Promise; + getApiOperationDetails( + apiSpecificationPath: string, + method: string, + path: string + ): Promise; +} + export interface ToolContext { serviceId?: string; apiSpecificationPath?: string; diff --git a/typescript/src/tests/mockDevelopersApi.ts b/typescript/src/tests/mockDevelopersApi.ts new file mode 100644 index 0000000..e003796 --- /dev/null +++ b/typescript/src/tests/mockDevelopersApi.ts @@ -0,0 +1,10 @@ +import type { DevelopersApi } from '@/shared/types'; + +export const createMockApi = (): jest.Mocked => ({ + listServices: jest.fn(), + getDocumentation: jest.fn(), + getDocumentationSection: jest.fn(), + getDocumentationPage: jest.fn(), + getApiOperations: jest.fn(), + getApiOperationDetails: jest.fn(), +}); From aca4d76b46324a5b4fc51315a062c3f755d0d82d Mon Sep 17 00:00:00 2001 From: "Rocco, Marco" <7020500+ech0s7r@users.noreply.github.com> Date: Thu, 7 May 2026 11:53:09 -0400 Subject: [PATCH 2/3] Simplify tool signatures: move client into `ToolContext` --- .../__tests__/index.test.ts | 58 +++++++++++-------- typescript/src/modelcontextprotocol/index.ts | 3 +- .../tools/documentation/getDocumentation.ts | 15 ++--- .../documentation/getDocumentationPage.ts | 22 +++---- .../documentation/getDocumentationSection.ts | 17 ++---- .../tools/documentation/getOAuth10aGuide.ts | 22 +++---- .../tools/documentation/getOAuth20Guide.ts | 22 +++---- .../documentation/getOpenFinanceGuide.ts | 24 ++++---- typescript/src/shared/tools/index.ts | 26 ++++----- .../operations/getApiOperationDetails.ts | 28 +++------ .../tools/operations/getApiOperationList.ts | 20 +++---- .../shared/tools/services/getServicesList.ts | 22 +++---- typescript/src/shared/types.ts | 1 + 13 files changed, 122 insertions(+), 158 deletions(-) diff --git a/typescript/src/modelcontextprotocol/__tests__/index.test.ts b/typescript/src/modelcontextprotocol/__tests__/index.test.ts index d884d51..ce0937f 100644 --- a/typescript/src/modelcontextprotocol/__tests__/index.test.ts +++ b/typescript/src/modelcontextprotocol/__tests__/index.test.ts @@ -38,7 +38,7 @@ describe('MastercardDevelopersAgentToolkit', () => { it('should list all tools with correct name/description when no config', async () => { const registeredTools = await listRegisteredTools({}); - const expectedTools = tools({}); + const expectedTools = tools(buildContext({})); expect(registeredTools).toHaveLength(expectedTools.length); expectedTools.forEach((expectedTool, index) => { @@ -60,10 +60,12 @@ describe('MastercardDevelopersAgentToolkit', () => { 'https://static.developer.mastercard.com/content/service/swagger/path.yaml', }); - const expectedTools = tools({ - serviceId: 'service', - apiSpecificationPath: '/service/swagger/path.yaml', - }).filter((tool) => tool.name !== 'get-services-list'); + const expectedTools = tools( + buildContext({ + apiSpecification: + 'https://static.developer.mastercard.com/content/service/swagger/path.yaml', + }) + ).filter((tool) => tool.name !== 'get-services-list'); expect(registeredTools).toHaveLength(expectedTools.length); expectedTools.forEach((expectedTool, index) => { @@ -84,9 +86,11 @@ describe('MastercardDevelopersAgentToolkit', () => { service: 'https://developer.mastercard.com/test-service/documentation/', }); - const expectedTools = tools({ serviceId: 'test-service' }).filter( - (tool) => tool.name !== 'get-services-list' - ); + const expectedTools = tools( + buildContext({ + service: 'https://developer.mastercard.com/test-service/documentation/', + }) + ).filter((tool) => tool.name !== 'get-services-list'); const registeredNames = registeredTools.map((tool) => tool.name); expect(registeredNames).not.toContain('get-services-list'); @@ -110,7 +114,7 @@ describe('buildContext function', () => { describe('success cases', () => { it('should return empty context when no config provided', () => { const result = buildContext({}); - expect(result).toEqual({}); + expect(result).toEqual(expect.objectContaining({})); }); it('should parse service URL and extract serviceId', () => { @@ -118,9 +122,11 @@ describe('buildContext function', () => { service: 'https://developer.mastercard.com/open-finance-us/documentation/', }); - expect(result).toEqual({ - serviceId: 'open-finance-us', - }); + expect(result).toEqual( + expect.objectContaining({ + serviceId: 'open-finance-us', + }) + ); }); it('should parse API specification URL and extract serviceId and apiSpecificationPath', () => { @@ -128,10 +134,12 @@ describe('buildContext function', () => { apiSpecification: 'https://static.developer.mastercard.com/content/test-service/swagger/api.yaml', }); - expect(result).toEqual({ - serviceId: 'test-service', - apiSpecificationPath: '/test-service/swagger/api.yaml', - }); + expect(result).toEqual( + expect.objectContaining({ + serviceId: 'test-service', + apiSpecificationPath: '/test-service/swagger/api.yaml', + }) + ); }); it('should handle nested API specification paths', () => { @@ -139,10 +147,12 @@ describe('buildContext function', () => { apiSpecification: 'https://static.developer.mastercard.com/content/payment-gateway/swagger/nested/spec.yaml', }); - expect(result).toEqual({ - serviceId: 'payment-gateway', - apiSpecificationPath: '/payment-gateway/swagger/nested/spec.yaml', - }); + expect(result).toEqual( + expect.objectContaining({ + serviceId: 'payment-gateway', + apiSpecificationPath: '/payment-gateway/swagger/nested/spec.yaml', + }) + ); }); it('should handle service IDs with hyphens and numbers', () => { @@ -150,9 +160,11 @@ describe('buildContext function', () => { service: 'https://developer.mastercard.com/open-finance-us-v2/documentation/', }); - expect(result).toEqual({ - serviceId: 'open-finance-us-v2', - }); + expect(result).toEqual( + expect.objectContaining({ + serviceId: 'open-finance-us-v2', + }) + ); }); }); diff --git a/typescript/src/modelcontextprotocol/index.ts b/typescript/src/modelcontextprotocol/index.ts index b0f1b3b..122983a 100644 --- a/typescript/src/modelcontextprotocol/index.ts +++ b/typescript/src/modelcontextprotocol/index.ts @@ -1,4 +1,5 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { defaultDevelopersApi } from '@/shared/api'; import { tools } from '@/shared/tools'; import { ToolContext } from '@/shared/types'; import { version } from '../../package.json'; @@ -64,7 +65,7 @@ export class MastercardDevelopersAgentToolkit extends McpServer { export function buildContext( config: MastercardDevelopersAgentToolkitConfig ): ToolContext { - const context: ToolContext = {}; + const context: ToolContext = { client: defaultDevelopersApi }; if (config.service != null) { const serviceId = parseServiceIdFromUrl(config.service); if (serviceId == null) { diff --git a/typescript/src/shared/tools/documentation/getDocumentation.ts b/typescript/src/shared/tools/documentation/getDocumentation.ts index f2bc091..e5be189 100644 --- a/typescript/src/shared/tools/documentation/getDocumentation.ts +++ b/typescript/src/shared/tools/documentation/getDocumentation.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { DevelopersApi, Tool, ToolContext } from '@/shared/types'; +import { Tool, ToolContext } from '@/shared/types'; const getDescription = (context: ToolContext): string => { const baseDescription = `Provides an overview of all available documentation for a specific Mastercard service @@ -34,17 +34,14 @@ export const getParameters = (context: ToolContext): z.ZodObject => { export const execute = async ( context: ToolContext, - developersApi: DevelopersApi, params: z.infer> ): Promise => { - const serviceId = context.serviceId || params.serviceId; - return await developersApi.getDocumentation(serviceId); + return await context.client.getDocumentation( + context.serviceId || params.serviceId + ); }; -export const getDocumentation = ( - context: ToolContext, - developersApi: DevelopersApi -): Tool => ({ +export const getDocumentation = (context: ToolContext): Tool => ({ name: 'get-documentation', title: 'Get Documentation', description: getDescription(context), @@ -55,5 +52,5 @@ export const getDocumentation = ( idempotentHint: true, openWorldHint: true, }, - execute: (params) => execute(context, developersApi, params), + execute: (params) => execute(context, params), }); diff --git a/typescript/src/shared/tools/documentation/getDocumentationPage.ts b/typescript/src/shared/tools/documentation/getDocumentationPage.ts index 64e7ca0..01a75f9 100644 --- a/typescript/src/shared/tools/documentation/getDocumentationPage.ts +++ b/typescript/src/shared/tools/documentation/getDocumentationPage.ts @@ -1,14 +1,14 @@ import { z } from 'zod'; -import { DevelopersApi, Tool, ToolContext } from '@/shared/types'; +import { Tool, ToolContext } from '@/shared/types'; -const getDescription = (_context: ToolContext): string => { +const getDescription = (): string => { return `Retrieves the complete content of a specific documentation page. Takes one argument: - pagePath (str): The full path to the documentation page (e.g., '/send/documentation/use-cases/index.md')`; }; -export const getParameters = (_context: ToolContext): z.ZodObject => { +export const getParameters = (): z.ZodObject => { return z.object({ pagePath: z .string() @@ -21,26 +21,22 @@ export const getParameters = (_context: ToolContext): z.ZodObject => { }; export const execute = async ( - _context: ToolContext, - developersApi: DevelopersApi, + context: ToolContext, params: z.infer> ): Promise => { - return await developersApi.getDocumentationPage(params.pagePath); + return await context.client.getDocumentationPage(params.pagePath); }; -export const getDocumentationPage = ( - context: ToolContext, - developersApi: DevelopersApi -): Tool => ({ +export const getDocumentationPage = (context: ToolContext): Tool => ({ name: 'get-documentation-page', title: 'Get Documentation Page', - description: getDescription(context), - parameters: getParameters(context), + description: getDescription(), + parameters: getParameters(), annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true, }, - execute: (params) => execute(context, developersApi, params), + execute: (params) => execute(context, params), }); diff --git a/typescript/src/shared/tools/documentation/getDocumentationSection.ts b/typescript/src/shared/tools/documentation/getDocumentationSection.ts index 76f86c1..8b644f0 100644 --- a/typescript/src/shared/tools/documentation/getDocumentationSection.ts +++ b/typescript/src/shared/tools/documentation/getDocumentationSection.ts @@ -1,9 +1,9 @@ import { z } from 'zod'; -import { DevelopersApi, Tool, ToolContext } from '@/shared/types'; +import { Tool, ToolContext } from '@/shared/types'; const getDescription = (context: ToolContext): string => { const baseDescription = ` -Retrieves the complete content for a specific documentation section. +Retrieves the complete content for a specific documentation section. IMPORTANT: A section is not a single page, but rather a collection of pages that are grouped together. `; @@ -51,20 +51,15 @@ export const getParameters = (context: ToolContext): z.ZodObject => { export const execute = async ( context: ToolContext, - developersApi: DevelopersApi, params: z.infer> ): Promise => { - const serviceId = context.serviceId || params.serviceId; - return await developersApi.getDocumentationSection( - serviceId, + return await context.client.getDocumentationSection( + context.serviceId || params.serviceId, params.sectionId ); }; -export const getDocumentationSection = ( - context: ToolContext, - developersApi: DevelopersApi -): Tool => ({ +export const getDocumentationSection = (context: ToolContext): Tool => ({ name: 'get-documentation-section-content', title: 'Get Documentation Section Content', description: getDescription(context), @@ -75,5 +70,5 @@ export const getDocumentationSection = ( idempotentHint: true, openWorldHint: true, }, - execute: (params) => execute(context, developersApi, params), + execute: (params) => execute(context, params), }); diff --git a/typescript/src/shared/tools/documentation/getOAuth10aGuide.ts b/typescript/src/shared/tools/documentation/getOAuth10aGuide.ts index 0334668..c1482c9 100644 --- a/typescript/src/shared/tools/documentation/getOAuth10aGuide.ts +++ b/typescript/src/shared/tools/documentation/getOAuth10aGuide.ts @@ -1,14 +1,14 @@ import { z } from 'zod'; import fetch from 'node-fetch'; -import { DevelopersApi, Tool, ToolContext } from '@/shared/types'; +import { Tool, ToolContext } from '@/shared/types'; -const getDescription = (_context: ToolContext): string => { +const getDescription = (): string => { return `Retrieves the comprehensive OAuth 1.0a integration guide including step-by-step instructions, code examples, and best practices for Mastercard APIs. Optionally specify a programming language to get language-specific examples and guidance.`; }; -export const getParameters = (_context: ToolContext): z.ZodObject => { +export const getParameters = (): z.ZodObject => { return z.object({ language: z .enum([ @@ -29,8 +29,7 @@ export const getParameters = (_context: ToolContext): z.ZodObject => { }; export const execute = async ( - _context: ToolContext, - developersApi: DevelopersApi, + context: ToolContext, params: z.infer> ): Promise => { const basePath = @@ -69,22 +68,19 @@ export const execute = async ( } // Fallback to fetching the general OAuth 1.0a guide - return await developersApi.getDocumentationPage(basePath); + return await context.client.getDocumentationPage(basePath); }; -export const getOAuth10aGuide = ( - context: ToolContext, - developersApi: DevelopersApi -): Tool => ({ +export const getOAuth10aGuide = (context: ToolContext): Tool => ({ name: 'get-oauth10a-integration-guide', title: 'Get OAuth 1.0a Integration Guide', - description: getDescription(context), - parameters: getParameters(context), + description: getDescription(), + parameters: getParameters(), annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true, }, - execute: (params) => execute(context, developersApi, params), + execute: (params) => execute(context, params), }); diff --git a/typescript/src/shared/tools/documentation/getOAuth20Guide.ts b/typescript/src/shared/tools/documentation/getOAuth20Guide.ts index adccf25..c5f716e 100644 --- a/typescript/src/shared/tools/documentation/getOAuth20Guide.ts +++ b/typescript/src/shared/tools/documentation/getOAuth20Guide.ts @@ -1,14 +1,14 @@ import { z } from 'zod'; import fetch from 'node-fetch'; -import { DevelopersApi, Tool, ToolContext } from '@/shared/types'; +import { Tool, ToolContext } from '@/shared/types'; -const getDescription = (_context: ToolContext): string => { +const getDescription = (): string => { return `Retrieves the comprehensive OAuth 2.0 integration guide including step-by-step instructions, code examples, and best practices for Mastercard APIs. Optionally specify a programming language to get language-specific examples and guidance.`; }; -export const getParameters = (_context: ToolContext): z.ZodObject => { +export const getParameters = (): z.ZodObject => { return z.object({ language: z .enum(['java', 'kotlin', 'javascript', 'typescript', 'others']) @@ -20,8 +20,7 @@ export const getParameters = (_context: ToolContext): z.ZodObject => { }; export const execute = async ( - _context: ToolContext, - developersApi: DevelopersApi, + context: ToolContext, params: z.infer> ): Promise => { const basePath = @@ -51,22 +50,19 @@ export const execute = async ( } // Fallback to fetching the general OAuth 2.0 guide - return await developersApi.getDocumentationPage(basePath); + return await context.client.getDocumentationPage(basePath); }; -export const getOAuth20Guide = ( - context: ToolContext, - developersApi: DevelopersApi -): Tool => ({ +export const getOAuth20Guide = (context: ToolContext): Tool => ({ name: 'get-oauth20-integration-guide', title: 'Get OAuth 2.0 Integration Guide', - description: getDescription(context), - parameters: getParameters(context), + description: getDescription(), + parameters: getParameters(), annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true, }, - execute: (params) => execute(context, developersApi, params), + execute: (params) => execute(context, params), }); diff --git a/typescript/src/shared/tools/documentation/getOpenFinanceGuide.ts b/typescript/src/shared/tools/documentation/getOpenFinanceGuide.ts index 3632f30..9f77b5a 100644 --- a/typescript/src/shared/tools/documentation/getOpenFinanceGuide.ts +++ b/typescript/src/shared/tools/documentation/getOpenFinanceGuide.ts @@ -1,38 +1,34 @@ import { z } from 'zod'; -import { DevelopersApi, Tool, ToolContext } from '@/shared/types'; +import { Tool, ToolContext } from '@/shared/types'; -const getDescription = (_context: ToolContext): string => { - return `Retrieves the comprehensive Open Finance (previously known as Open Banking) integration +const getDescription = (): string => { + return `Retrieves the comprehensive Open Finance (previously known as Open Banking) integration guide including setup instructions, API usage examples, and implementation best practices.`; }; -export const getParameters = (_context: ToolContext): z.ZodObject => { +export const getParameters = (): z.ZodObject => { return z.object({}); }; export const execute = async ( - _context: ToolContext, - developersApi: DevelopersApi, + context: ToolContext, _params: z.infer> ): Promise => { - return await developersApi.getDocumentationPage( + return await context.client.getDocumentationPage( '/open-finance-us/documentation/quick-start-guide/index.md' ); }; -export const getOpenFinanceGuide = ( - context: ToolContext, - developersApi: DevelopersApi -): Tool => ({ +export const getOpenFinanceGuide = (context: ToolContext): Tool => ({ name: 'get-openfinance-integration-guide', title: 'Get Open Finance Integration Guide', - description: getDescription(context), - parameters: getParameters(context), + description: getDescription(), + parameters: getParameters(), annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true, }, - execute: (params) => execute(context, developersApi, params), + execute: (params) => execute(context, params), }); diff --git a/typescript/src/shared/tools/index.ts b/typescript/src/shared/tools/index.ts index 7c5e85c..d1b7ffd 100644 --- a/typescript/src/shared/tools/index.ts +++ b/typescript/src/shared/tools/index.ts @@ -1,5 +1,4 @@ -import { defaultDevelopersApi } from '@/shared/api'; -import { DevelopersApi, Tool, ToolContext } from '@/shared/types'; +import { Tool, ToolContext } from '@/shared/types'; // Documentation tools import { getDocumentation } from '@/shared/tools/documentation/getDocumentation'; @@ -16,22 +15,19 @@ import { getServicesList } from '@/shared/tools/services/getServicesList'; import { getApiOperationList } from '@/shared/tools/operations/getApiOperationList'; import { getApiOperationDetails } from '@/shared/tools/operations/getApiOperationDetails'; -export const tools = ( - context: ToolContext = {}, - developersApi: DevelopersApi = defaultDevelopersApi -): Tool[] => [ +export const tools = (context: ToolContext): Tool[] => [ // Services - getServicesList(context, developersApi), + getServicesList(context), // Documentation - getDocumentation(context, developersApi), - getDocumentationSection(context, developersApi), - getDocumentationPage(context, developersApi), - getOAuth10aGuide(context, developersApi), - getOAuth20Guide(context, developersApi), - getOpenFinanceGuide(context, developersApi), + getDocumentation(context), + getDocumentationSection(context), + getDocumentationPage(context), + getOAuth10aGuide(context), + getOAuth20Guide(context), + getOpenFinanceGuide(context), // API Operations - getApiOperationList(context, developersApi), - getApiOperationDetails(context, developersApi), + getApiOperationList(context), + getApiOperationDetails(context), ]; diff --git a/typescript/src/shared/tools/operations/getApiOperationDetails.ts b/typescript/src/shared/tools/operations/getApiOperationDetails.ts index 87c8f9e..a3a8aff 100644 --- a/typescript/src/shared/tools/operations/getApiOperationDetails.ts +++ b/typescript/src/shared/tools/operations/getApiOperationDetails.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { DevelopersApi, Tool, ToolContext } from '@/shared/types'; +import { Tool, ToolContext } from '@/shared/types'; const getDescription = (context: ToolContext): string => { const baseDescription = `Provides detailed information about a specific API operation including parameter definitions, @@ -61,28 +61,16 @@ export const getParameters = (context: ToolContext): z.ZodObject => { export const execute = async ( context: ToolContext, - developersApi: DevelopersApi, params: z.infer> ): Promise => { - if (context.apiSpecificationPath) { - return await developersApi.getApiOperationDetails( - context.apiSpecificationPath, - params.method, - params.path - ); - } else { - return await developersApi.getApiOperationDetails( - params.apiSpecificationPath, - params.method, - params.path - ); - } + return await context.client.getApiOperationDetails( + context.apiSpecificationPath || params.apiSpecificationPath, + params.method, + params.path + ); }; -export const getApiOperationDetails = ( - context: ToolContext, - developersApi: DevelopersApi -): Tool => ({ +export const getApiOperationDetails = (context: ToolContext): Tool => ({ name: 'get-api-operation-details', title: 'Get API Operation Details', description: getDescription(context), @@ -93,5 +81,5 @@ export const getApiOperationDetails = ( idempotentHint: true, openWorldHint: true, }, - execute: (params) => execute(context, developersApi, params), + execute: (params) => execute(context, params), }); diff --git a/typescript/src/shared/tools/operations/getApiOperationList.ts b/typescript/src/shared/tools/operations/getApiOperationList.ts index e2b6096..2a9fc15 100644 --- a/typescript/src/shared/tools/operations/getApiOperationList.ts +++ b/typescript/src/shared/tools/operations/getApiOperationList.ts @@ -1,8 +1,8 @@ import { z } from 'zod'; -import { DevelopersApi, Tool, ToolContext } from '@/shared/types'; +import { Tool, ToolContext } from '@/shared/types'; const getDescription = (context: ToolContext): string => { - const baseDescription = `Provides a summary of all API operations for a specific Mastercard API + const baseDescription = `Provides a summary of all API operations for a specific Mastercard API specification including HTTP methods, request paths, titles, and descriptions.`; if (context.apiSpecificationPath) { @@ -34,20 +34,14 @@ export const getParameters = (context: ToolContext): z.ZodObject => { export const execute = async ( context: ToolContext, - developersApi: DevelopersApi, params: z.infer> ): Promise => { - if (context.apiSpecificationPath) { - return await developersApi.getApiOperations(context.apiSpecificationPath); - } else { - return await developersApi.getApiOperations(params.apiSpecificationPath); - } + return await context.client.getApiOperations( + context.apiSpecificationPath || params.apiSpecificationPath + ); }; -export const getApiOperationList = ( - context: ToolContext, - developersApi: DevelopersApi -): Tool => ({ +export const getApiOperationList = (context: ToolContext): Tool => ({ name: 'get-api-operation-list', title: 'Get API Operation List', description: getDescription(context), @@ -58,5 +52,5 @@ export const getApiOperationList = ( idempotentHint: true, openWorldHint: true, }, - execute: (params) => execute(context, developersApi, params), + execute: (params) => execute(context, params), }); diff --git a/typescript/src/shared/tools/services/getServicesList.ts b/typescript/src/shared/tools/services/getServicesList.ts index 6de3f0a..0a83835 100644 --- a/typescript/src/shared/tools/services/getServicesList.ts +++ b/typescript/src/shared/tools/services/getServicesList.ts @@ -1,38 +1,34 @@ import { z } from 'zod'; -import { DevelopersApi, Tool, ToolContext } from '@/shared/types'; +import { Tool, ToolContext } from '@/shared/types'; -const getDescription = (_context: ToolContext): string => { +const getDescription = (): string => { return `Lists all available Mastercard Developers Products and Services with their basic information including title, description, and service id. IMPORTANT: The response contains both 'Products' (business offerings) and 'Services' (technical APIs with serviceIds). Use "serviceId" for each service for any tools that require serviceId as the parameter. `; }; -export const getParameters = (_context: ToolContext): z.ZodObject => { +export const getParameters = (): z.ZodObject => { return z.object({}); }; export const execute = async ( - _context: ToolContext, - developersApi: DevelopersApi, + context: ToolContext, _params: z.infer> ): Promise => { - return await developersApi.listServices(); + return await context.client.listServices(); }; -export const getServicesList = ( - context: ToolContext, - developersApi: DevelopersApi -): Tool => ({ +export const getServicesList = (context: ToolContext): Tool => ({ name: 'get-services-list', title: 'Get Services List', - description: getDescription(context), - parameters: getParameters(context), + description: getDescription(), + parameters: getParameters(), annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true, }, - execute: (params) => execute(context, developersApi, params), + execute: (params) => execute(context, params), }); diff --git a/typescript/src/shared/types.ts b/typescript/src/shared/types.ts index 0b72ecc..9eca094 100644 --- a/typescript/src/shared/types.ts +++ b/typescript/src/shared/types.ts @@ -27,6 +27,7 @@ export interface DevelopersApi { } export interface ToolContext { + client: DevelopersApi; serviceId?: string; apiSpecificationPath?: string; } From d4129ccf7c72c5f89a768a7085da45cafcbb8409 Mon Sep 17 00:00:00 2001 From: "Rocco, Marco" <7020500+ech0s7r@users.noreply.github.com> Date: Thu, 7 May 2026 12:09:50 -0400 Subject: [PATCH 3/3] Update test cases to include client context in execute function --- .../__tests__/getDocumentation.test.ts | 17 +++++++++--- .../__tests__/getDocumentationPage.test.ts | 7 +++-- .../__tests__/getDocumentationSection.test.ts | 27 ++++++++++++------- .../__tests__/getOAuth10aGuide.test.ts | 14 +++++++--- .../__tests__/getOAuth20Guide.test.ts | 16 +++++++---- .../__tests__/getOpenFinanceGuide.test.ts | 4 +-- .../__tests__/getApiOperationDetails.test.ts | 19 +++++++------ .../__tests__/getApiOperationList.test.ts | 15 ++++++----- .../__tests__/getServicesList.test.ts | 4 +-- 9 files changed, 81 insertions(+), 42 deletions(-) diff --git a/typescript/src/shared/tools/documentation/__tests__/getDocumentation.test.ts b/typescript/src/shared/tools/documentation/__tests__/getDocumentation.test.ts index 57094ce..f4d8ab0 100644 --- a/typescript/src/shared/tools/documentation/__tests__/getDocumentation.test.ts +++ b/typescript/src/shared/tools/documentation/__tests__/getDocumentation.test.ts @@ -15,7 +15,10 @@ describe('execute', () => { const mockResult = 'mock documentation'; mockApi.getDocumentation.mockResolvedValue(mockResult); - const result = await execute({}, mockApi, { serviceId: 'test-service' }); + const result = await execute( + { client: mockApi }, + { serviceId: 'test-service' } + ); expect(mockApi.getDocumentation).toHaveBeenCalledWith('test-service'); expect(result).toBe(mockResult); @@ -25,7 +28,10 @@ describe('execute', () => { const mockResult = 'mock documentation'; mockApi.getDocumentation.mockResolvedValue(mockResult); - const result = await execute({ serviceId: 'context-service' }, mockApi, {}); + const result = await execute( + { client: mockApi, serviceId: 'context-service' }, + {} + ); expect(mockApi.getDocumentation).toHaveBeenCalledWith('context-service'); expect(result).toBe(mockResult); @@ -34,7 +40,7 @@ describe('execute', () => { describe('getParameters', () => { it('should return the correct parameters if no context', () => { - const parameters = getParameters({}); + const parameters = getParameters({ client: mockApi }); const fields = Object.keys(parameters.shape); expect(fields).toEqual(['serviceId']); @@ -42,7 +48,10 @@ describe('getParameters', () => { }); it('should return the correct parameters if serviceId is specified in context', () => { - const parameters = getParameters({ serviceId: 'test-service' }); + const parameters = getParameters({ + client: mockApi, + serviceId: 'test-service', + }); const fields = Object.keys(parameters.shape); expect(fields).toEqual([]); diff --git a/typescript/src/shared/tools/documentation/__tests__/getDocumentationPage.test.ts b/typescript/src/shared/tools/documentation/__tests__/getDocumentationPage.test.ts index 1b73cce..6eb0a30 100644 --- a/typescript/src/shared/tools/documentation/__tests__/getDocumentationPage.test.ts +++ b/typescript/src/shared/tools/documentation/__tests__/getDocumentationPage.test.ts @@ -15,7 +15,10 @@ describe('execute', () => { const mockResult = 'mock documentation page content'; mockApi.getDocumentationPage.mockResolvedValue(mockResult); - const result = await execute({}, mockApi, { pagePath: '/test/page.md' }); + const result = await execute( + { client: mockApi }, + { pagePath: '/test/page.md' } + ); expect(mockApi.getDocumentationPage).toHaveBeenCalledWith('/test/page.md'); expect(result).toBe(mockResult); @@ -24,7 +27,7 @@ describe('execute', () => { describe('getParameters', () => { it('should return the correct parameters if no context', () => { - const parameters = getParameters({}); + const parameters = getParameters({ client: mockApi }); const fields = Object.keys(parameters.shape); expect(fields).toEqual(['pagePath']); diff --git a/typescript/src/shared/tools/documentation/__tests__/getDocumentationSection.test.ts b/typescript/src/shared/tools/documentation/__tests__/getDocumentationSection.test.ts index 8cee7d1..69c3c96 100644 --- a/typescript/src/shared/tools/documentation/__tests__/getDocumentationSection.test.ts +++ b/typescript/src/shared/tools/documentation/__tests__/getDocumentationSection.test.ts @@ -15,10 +15,13 @@ describe('execute', () => { const mockResult = 'mock documentation section content'; mockApi.getDocumentationSection.mockResolvedValue(mockResult); - const result = await execute({}, mockApi, { - serviceId: 'test-service', - sectionId: 'test-section', - }); + const result = await execute( + { client: mockApi }, + { + serviceId: 'test-service', + sectionId: 'test-section', + } + ); expect(mockApi.getDocumentationSection).toHaveBeenCalledWith( 'test-service', @@ -31,9 +34,12 @@ describe('execute', () => { const mockResult = 'mock documentation section content'; mockApi.getDocumentationSection.mockResolvedValue(mockResult); - const result = await execute({ serviceId: 'context-service' }, mockApi, { - sectionId: 'test-section', - }); + const result = await execute( + { client: mockApi, serviceId: 'context-service' }, + { + sectionId: 'test-section', + } + ); expect(mockApi.getDocumentationSection).toHaveBeenCalledWith( 'context-service', @@ -45,7 +51,7 @@ describe('execute', () => { describe('getParameters', () => { it('should return the correct parameters if no context', () => { - const parameters = getParameters({}); + const parameters = getParameters({ client: mockApi }); const fields = Object.keys(parameters.shape); expect(fields).toEqual(['serviceId', 'sectionId']); @@ -53,7 +59,10 @@ describe('getParameters', () => { }); it('should return the correct parameters if serviceId is specified in context', () => { - const parameters = getParameters({ serviceId: 'test-service' }); + const parameters = getParameters({ + client: mockApi, + serviceId: 'test-service', + }); const fields = Object.keys(parameters.shape); expect(fields).toEqual(['sectionId']); diff --git a/typescript/src/shared/tools/documentation/__tests__/getOAuth10aGuide.test.ts b/typescript/src/shared/tools/documentation/__tests__/getOAuth10aGuide.test.ts index 51bc616..fb48f68 100644 --- a/typescript/src/shared/tools/documentation/__tests__/getOAuth10aGuide.test.ts +++ b/typescript/src/shared/tools/documentation/__tests__/getOAuth10aGuide.test.ts @@ -21,7 +21,10 @@ describe('execute', () => { const mockResult = 'mock OAuth 1.0a guide content'; mockApi.getDocumentationPage.mockResolvedValue(mockResult); - const result = await execute({}, mockApi, { language: language as any }); + const result = await execute( + { client: mockApi }, + { language: language as any } + ); expect(mockApi.getDocumentationPage).toHaveBeenCalledWith( '/platform/documentation/authentication/using-oauth-1a-to-access-mastercard-apis/index.md' @@ -47,7 +50,10 @@ describe('execute', () => { text: jest.fn().mockResolvedValue(mockGithubContent), } as any); - const result = await execute({}, mockApi, { language: language as any }); + const result = await execute( + { client: mockApi }, + { language: language as any } + ); expect(mockFetch).toHaveBeenCalledWith( `https://raw.githubusercontent.com/Mastercard/${expectedRepo}/refs/heads/main/README.md` @@ -63,7 +69,7 @@ describe('execute', () => { } as any); mockApi.getDocumentationPage.mockResolvedValue(mockApiResult); - const result = await execute({}, mockApi, { language: 'java' }); + const result = await execute({ client: mockApi }, { language: 'java' }); expect(mockFetch).toHaveBeenCalledWith( 'https://raw.githubusercontent.com/Mastercard/oauth1-signer-java/refs/heads/main/README.md' @@ -77,7 +83,7 @@ describe('execute', () => { describe('getParameters', () => { it('should return the correct parameters if no context', () => { - const parameters = getParameters({}); + const parameters = getParameters({ client: mockApi }); const fields = Object.keys(parameters.shape); expect(fields).toEqual(['language']); diff --git a/typescript/src/shared/tools/documentation/__tests__/getOAuth20Guide.test.ts b/typescript/src/shared/tools/documentation/__tests__/getOAuth20Guide.test.ts index b5e4b36..55f211c 100644 --- a/typescript/src/shared/tools/documentation/__tests__/getOAuth20Guide.test.ts +++ b/typescript/src/shared/tools/documentation/__tests__/getOAuth20Guide.test.ts @@ -21,7 +21,10 @@ describe('execute', () => { const mockResult = 'mock OAuth 2.0 guide content'; mockApi.getDocumentationPage.mockResolvedValue(mockResult); - const result = await execute({}, mockApi, { language: language as any }); + const result = await execute( + { client: mockApi }, + { language: language as any } + ); expect(mockApi.getDocumentationPage).toHaveBeenCalledWith( '/platform/documentation/authentication/using-oauth-2-to-access-mastercard-apis/index.md' @@ -44,7 +47,10 @@ describe('execute', () => { text: jest.fn().mockResolvedValue(mockGithubContent), } as any); - const result = await execute({}, mockApi, { language: language as any }); + const result = await execute( + { client: mockApi }, + { language: language as any } + ); expect(mockFetch).toHaveBeenCalledWith( `https://raw.githubusercontent.com/Mastercard/${expectedRepo}/refs/heads/main/README.md` @@ -58,7 +64,7 @@ describe('execute', () => { mockFetch.mockResolvedValue({ ok: false } as any); mockApi.getDocumentationPage.mockResolvedValue(mockApiResult); - const result = await execute({}, mockApi, { language: 'java' }); + const result = await execute({ client: mockApi }, { language: 'java' }); expect(mockFetch).toHaveBeenCalledWith( 'https://raw.githubusercontent.com/Mastercard/oauth2-client-java/refs/heads/main/README.md' @@ -72,7 +78,7 @@ describe('execute', () => { describe('getParameters', () => { it('should return the correct parameters', () => { - const parameters = getParameters({}); + const parameters = getParameters({ client: mockApi }); const fields = Object.keys(parameters.shape); expect(fields).toEqual(['language']); @@ -82,7 +88,7 @@ describe('getParameters', () => { }); it('should not accept languages unsupported by OAuth 2.0', () => { - const schema = getParameters({}); + const schema = getParameters({ client: mockApi }); expect(schema.safeParse({ language: 'c#' }).success).toBe(false); expect(schema.safeParse({ language: 'python' }).success).toBe(false); expect(schema.safeParse({ language: 'golang' }).success).toBe(false); diff --git a/typescript/src/shared/tools/documentation/__tests__/getOpenFinanceGuide.test.ts b/typescript/src/shared/tools/documentation/__tests__/getOpenFinanceGuide.test.ts index 8116ad4..352a3a7 100644 --- a/typescript/src/shared/tools/documentation/__tests__/getOpenFinanceGuide.test.ts +++ b/typescript/src/shared/tools/documentation/__tests__/getOpenFinanceGuide.test.ts @@ -15,7 +15,7 @@ describe('execute', () => { const mockResult = 'mock Open Finance guide content'; mockApi.getDocumentationPage.mockResolvedValue(mockResult); - const result = await execute({}, mockApi, {}); + const result = await execute({ client: mockApi }, {}); expect(mockApi.getDocumentationPage).toHaveBeenCalledWith( '/open-finance-us/documentation/quick-start-guide/index.md' @@ -26,7 +26,7 @@ describe('execute', () => { describe('getParameters', () => { it('should return the correct parameters if no context', () => { - const parameters = getParameters({}); + const parameters = getParameters({ client: mockApi }); const fields = Object.keys(parameters.shape); expect(fields).toEqual([]); diff --git a/typescript/src/shared/tools/operations/__tests__/getApiOperationDetails.test.ts b/typescript/src/shared/tools/operations/__tests__/getApiOperationDetails.test.ts index 3e00488..0a066bc 100644 --- a/typescript/src/shared/tools/operations/__tests__/getApiOperationDetails.test.ts +++ b/typescript/src/shared/tools/operations/__tests__/getApiOperationDetails.test.ts @@ -15,11 +15,14 @@ describe('execute', () => { const mockResult = 'mock operation details'; mockApi.getApiOperationDetails.mockResolvedValue(mockResult); - const result = await execute({}, mockApi, { - apiSpecificationPath: '/test/path.yaml', - method: 'GET', - path: '/test/endpoint', - }); + const result = await execute( + { client: mockApi }, + { + apiSpecificationPath: '/test/path.yaml', + method: 'GET', + path: '/test/endpoint', + } + ); expect(mockApi.getApiOperationDetails).toHaveBeenCalledWith( '/test/path.yaml', @@ -34,8 +37,7 @@ describe('execute', () => { mockApi.getApiOperationDetails.mockResolvedValue(mockResult); const result = await execute( - { apiSpecificationPath: '/context/path.yaml' }, - mockApi, + { client: mockApi, apiSpecificationPath: '/context/path.yaml' }, { method: 'POST', path: '/context/endpoint', @@ -53,7 +55,7 @@ describe('execute', () => { describe('getParameters', () => { it('should return the correct parameters if no context', () => { - const parameters = getParameters({}); + const parameters = getParameters({ client: mockApi }); const fields = Object.keys(parameters.shape); expect(fields).toEqual(['apiSpecificationPath', 'method', 'path']); @@ -62,6 +64,7 @@ describe('getParameters', () => { it('should return the correct parameters if apiSpecificationPath is specified in context', () => { const parameters = getParameters({ + client: mockApi, apiSpecificationPath: '/test/path.yaml', }); diff --git a/typescript/src/shared/tools/operations/__tests__/getApiOperationList.test.ts b/typescript/src/shared/tools/operations/__tests__/getApiOperationList.test.ts index 21cc1de..832d8d8 100644 --- a/typescript/src/shared/tools/operations/__tests__/getApiOperationList.test.ts +++ b/typescript/src/shared/tools/operations/__tests__/getApiOperationList.test.ts @@ -15,9 +15,12 @@ describe('execute', () => { const mockResult = 'mock operations list'; mockApi.getApiOperations.mockResolvedValue(mockResult); - const result = await execute({}, mockApi, { - apiSpecificationPath: '/test/path.yaml', - }); + const result = await execute( + { client: mockApi }, + { + apiSpecificationPath: '/test/path.yaml', + } + ); expect(mockApi.getApiOperations).toHaveBeenCalledWith('/test/path.yaml'); expect(result).toBe(mockResult); @@ -28,8 +31,7 @@ describe('execute', () => { mockApi.getApiOperations.mockResolvedValue(mockResult); const result = await execute( - { apiSpecificationPath: '/context/path.yaml' }, - mockApi, + { client: mockApi, apiSpecificationPath: '/context/path.yaml' }, {} ); @@ -40,7 +42,7 @@ describe('execute', () => { describe('getParameters', () => { it('should return the correct parameters if no context', () => { - const parameters = getParameters({}); + const parameters = getParameters({ client: mockApi }); const fields = Object.keys(parameters.shape); expect(fields).toEqual(['apiSpecificationPath']); @@ -49,6 +51,7 @@ describe('getParameters', () => { it('should return the correct parameters if apiSpecificationPath is specified in context', () => { const parameters = getParameters({ + client: mockApi, apiSpecificationPath: '/test/path.yaml', }); diff --git a/typescript/src/shared/tools/services/__tests__/getServicesList.test.ts b/typescript/src/shared/tools/services/__tests__/getServicesList.test.ts index f0bf2e8..a0c0448 100644 --- a/typescript/src/shared/tools/services/__tests__/getServicesList.test.ts +++ b/typescript/src/shared/tools/services/__tests__/getServicesList.test.ts @@ -15,7 +15,7 @@ describe('execute', () => { const mockResult = 'mock services list'; mockApi.listServices.mockResolvedValue(mockResult); - const result = await execute({}, mockApi, {}); + const result = await execute({ client: mockApi }, {}); expect(mockApi.listServices).toHaveBeenCalledTimes(1); expect(result).toBe(mockResult); @@ -24,7 +24,7 @@ describe('execute', () => { describe('getParameters', () => { it('should return the correct parameters if no context', () => { - const parameters = getParameters({}); + const parameters = getParameters({ client: mockApi }); const fields = Object.keys(parameters.shape); expect(fields).toEqual([]);