From cc4445f059907b8a11812a7b4fcc5d83187191d1 Mon Sep 17 00:00:00 2001 From: "Rocco, Marco" <7020500+ech0s7r@users.noreply.github.com> Date: Fri, 8 May 2026 13:49:09 -0400 Subject: [PATCH 1/4] Support injectable GitHub transport in OAuth guide tools --- typescript/package-lock.json | 4 ++-- typescript/package.json | 2 +- .../src/shared/tools/documentation/getOAuth10aGuide.ts | 10 ++++++---- .../src/shared/tools/documentation/getOAuth20Guide.ts | 10 ++++++---- typescript/src/shared/types.ts | 1 + 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/typescript/package-lock.json b/typescript/package-lock.json index 238da22..c48eea4 100644 --- a/typescript/package-lock.json +++ b/typescript/package-lock.json @@ -1,12 +1,12 @@ { "name": "@mastercard/developers-agent-toolkit", - "version": "0.1.5", + "version": "0.1.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@mastercard/developers-agent-toolkit", - "version": "0.1.5", + "version": "0.1.6", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.27.1", diff --git a/typescript/package.json b/typescript/package.json index 5c5c0b7..a4be676 100644 --- a/typescript/package.json +++ b/typescript/package.json @@ -1,5 +1,5 @@ { - "version": "0.1.5", + "version": "0.1.6", "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/shared/tools/documentation/getOAuth10aGuide.ts b/typescript/src/shared/tools/documentation/getOAuth10aGuide.ts index c1482c9..c344dfd 100644 --- a/typescript/src/shared/tools/documentation/getOAuth10aGuide.ts +++ b/typescript/src/shared/tools/documentation/getOAuth10aGuide.ts @@ -59,10 +59,12 @@ export const execute = async ( 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(); + if (context.client.fetchGithubGuide !== undefined) { + const content = await context.client.fetchGithubGuide(githubUrl).catch(() => undefined); + if (content !== undefined) return content; + } else { + const response = await fetch(githubUrl); + if (response.ok) return await response.text(); } } } diff --git a/typescript/src/shared/tools/documentation/getOAuth20Guide.ts b/typescript/src/shared/tools/documentation/getOAuth20Guide.ts index c5f716e..d7c28d2 100644 --- a/typescript/src/shared/tools/documentation/getOAuth20Guide.ts +++ b/typescript/src/shared/tools/documentation/getOAuth20Guide.ts @@ -41,10 +41,12 @@ export const execute = async ( 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(); + if (context.client.fetchGithubGuide !== undefined) { + const content = await context.client.fetchGithubGuide(githubUrl).catch(() => undefined); + if (content !== undefined) return content; + } else { + const response = await fetch(githubUrl); + if (response.ok) return await response.text(); } } } diff --git a/typescript/src/shared/types.ts b/typescript/src/shared/types.ts index 9eca094..3c2a6ba 100644 --- a/typescript/src/shared/types.ts +++ b/typescript/src/shared/types.ts @@ -24,6 +24,7 @@ export interface DevelopersApi { method: string, path: string ): Promise; + fetchGithubGuide?(url: string): Promise; } export interface ToolContext { From d1f1d37055fe3d2e78e553db417d9b2931bd5615 Mon Sep 17 00:00:00 2001 From: "Rocco, Marco" <7020500+ech0s7r@users.noreply.github.com> Date: Fri, 8 May 2026 13:49:56 -0400 Subject: [PATCH 2/4] code format --- typescript/src/shared/tools/documentation/getOAuth10aGuide.ts | 4 +++- typescript/src/shared/tools/documentation/getOAuth20Guide.ts | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/typescript/src/shared/tools/documentation/getOAuth10aGuide.ts b/typescript/src/shared/tools/documentation/getOAuth10aGuide.ts index c344dfd..b110e55 100644 --- a/typescript/src/shared/tools/documentation/getOAuth10aGuide.ts +++ b/typescript/src/shared/tools/documentation/getOAuth10aGuide.ts @@ -60,7 +60,9 @@ export const execute = async ( if (repositoryName !== undefined) { const githubUrl = `https://raw.githubusercontent.com/Mastercard/${repositoryName}/refs/heads/main/README.md`; if (context.client.fetchGithubGuide !== undefined) { - const content = await context.client.fetchGithubGuide(githubUrl).catch(() => undefined); + const content = await context.client + .fetchGithubGuide(githubUrl) + .catch(() => undefined); if (content !== undefined) return content; } else { const response = await fetch(githubUrl); diff --git a/typescript/src/shared/tools/documentation/getOAuth20Guide.ts b/typescript/src/shared/tools/documentation/getOAuth20Guide.ts index d7c28d2..bdfdb2e 100644 --- a/typescript/src/shared/tools/documentation/getOAuth20Guide.ts +++ b/typescript/src/shared/tools/documentation/getOAuth20Guide.ts @@ -42,7 +42,9 @@ export const execute = async ( if (repositoryName !== undefined) { const githubUrl = `https://raw.githubusercontent.com/Mastercard/${repositoryName}/refs/heads/main/README.md`; if (context.client.fetchGithubGuide !== undefined) { - const content = await context.client.fetchGithubGuide(githubUrl).catch(() => undefined); + const content = await context.client + .fetchGithubGuide(githubUrl) + .catch(() => undefined); if (content !== undefined) return content; } else { const response = await fetch(githubUrl); From 86165e21e00289852b5a60e045e8b25196e967a2 Mon Sep 17 00:00:00 2001 From: "Rocco, Marco" <7020500+ech0s7r@users.noreply.github.com> Date: Fri, 8 May 2026 13:57:23 -0400 Subject: [PATCH 3/4] Extract common fetching logic into a shared function --- .../__tests__/githubReadme.test.ts | 59 +++++++++++++++++++ .../tools/documentation/getOAuth10aGuide.ts | 14 ++--- .../tools/documentation/getOAuth20Guide.ts | 14 ++--- .../tools/documentation/githubReadme.ts | 14 +++++ 4 files changed, 81 insertions(+), 20 deletions(-) create mode 100644 typescript/src/shared/tools/documentation/__tests__/githubReadme.test.ts create mode 100644 typescript/src/shared/tools/documentation/githubReadme.ts diff --git a/typescript/src/shared/tools/documentation/__tests__/githubReadme.test.ts b/typescript/src/shared/tools/documentation/__tests__/githubReadme.test.ts new file mode 100644 index 0000000..b179610 --- /dev/null +++ b/typescript/src/shared/tools/documentation/__tests__/githubReadme.test.ts @@ -0,0 +1,59 @@ +import { fetchGithubReadme } from '@/shared/tools/documentation/githubReadme'; +import { createMockApi } from '@/tests/mockDevelopersApi'; +import fetch from 'node-fetch'; + +jest.mock('node-fetch'); + +const mockFetch = fetch as jest.MockedFunction; +const mockApi = createMockApi(); + +describe('fetchGithubReadme', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('uses fetchGithubGuide when provided and returns content on success', async () => { + const fetchGithubGuide = jest.fn().mockResolvedValue('github content'); + + const result = await fetchGithubReadme('oauth1-signer-java', { + client: { ...mockApi, fetchGithubGuide }, + }); + + expect(fetchGithubGuide).toHaveBeenCalledWith( + 'https://raw.githubusercontent.com/Mastercard/oauth1-signer-java/refs/heads/main/README.md' + ); + expect(result).toBe('github content'); + }); + + it('returns undefined when fetchGithubGuide is provided but fails', async () => { + const fetchGithubGuide = jest.fn().mockRejectedValue(new Error('network error')); + + const result = await fetchGithubReadme('oauth1-signer-java', { + client: { ...mockApi, fetchGithubGuide }, + }); + + expect(result).toBeUndefined(); + }); + + it('uses plain fetch when fetchGithubGuide is not provided and returns content on success', async () => { + mockFetch.mockResolvedValue({ + ok: true, + text: jest.fn().mockResolvedValue('github content'), + } as any); + + const result = await fetchGithubReadme('oauth1-signer-java', { client: mockApi }); + + expect(mockFetch).toHaveBeenCalledWith( + 'https://raw.githubusercontent.com/Mastercard/oauth1-signer-java/refs/heads/main/README.md' + ); + expect(result).toBe('github content'); + }); + + it('returns undefined when plain fetch response is not ok', async () => { + mockFetch.mockResolvedValue({ ok: false } as any); + + const result = await fetchGithubReadme('oauth1-signer-java', { client: mockApi }); + + expect(result).toBeUndefined(); + }); +}); diff --git a/typescript/src/shared/tools/documentation/getOAuth10aGuide.ts b/typescript/src/shared/tools/documentation/getOAuth10aGuide.ts index b110e55..e436f02 100644 --- a/typescript/src/shared/tools/documentation/getOAuth10aGuide.ts +++ b/typescript/src/shared/tools/documentation/getOAuth10aGuide.ts @@ -1,6 +1,6 @@ import { z } from 'zod'; -import fetch from 'node-fetch'; import { Tool, ToolContext } from '@/shared/types'; +import { fetchGithubReadme } from './githubReadme'; const getDescription = (): string => { return `Retrieves the comprehensive OAuth 1.0a integration guide including step-by-step instructions, @@ -58,15 +58,9 @@ export const execute = async ( } if (repositoryName !== undefined) { - const githubUrl = `https://raw.githubusercontent.com/Mastercard/${repositoryName}/refs/heads/main/README.md`; - if (context.client.fetchGithubGuide !== undefined) { - const content = await context.client - .fetchGithubGuide(githubUrl) - .catch(() => undefined); - if (content !== undefined) return content; - } else { - const response = await fetch(githubUrl); - if (response.ok) return await response.text(); + const content = await fetchGithubReadme(repositoryName, context); + if (content !== undefined) { + return content; } } } diff --git a/typescript/src/shared/tools/documentation/getOAuth20Guide.ts b/typescript/src/shared/tools/documentation/getOAuth20Guide.ts index bdfdb2e..c925807 100644 --- a/typescript/src/shared/tools/documentation/getOAuth20Guide.ts +++ b/typescript/src/shared/tools/documentation/getOAuth20Guide.ts @@ -1,6 +1,6 @@ import { z } from 'zod'; -import fetch from 'node-fetch'; import { Tool, ToolContext } from '@/shared/types'; +import { fetchGithubReadme } from './githubReadme'; const getDescription = (): string => { return `Retrieves the comprehensive OAuth 2.0 integration guide including step-by-step instructions, @@ -40,15 +40,9 @@ export const execute = async ( } if (repositoryName !== undefined) { - const githubUrl = `https://raw.githubusercontent.com/Mastercard/${repositoryName}/refs/heads/main/README.md`; - if (context.client.fetchGithubGuide !== undefined) { - const content = await context.client - .fetchGithubGuide(githubUrl) - .catch(() => undefined); - if (content !== undefined) return content; - } else { - const response = await fetch(githubUrl); - if (response.ok) return await response.text(); + const content = await fetchGithubReadme(repositoryName, context); + if (content !== undefined) { + return content; } } } diff --git a/typescript/src/shared/tools/documentation/githubReadme.ts b/typescript/src/shared/tools/documentation/githubReadme.ts new file mode 100644 index 0000000..33d1cc1 --- /dev/null +++ b/typescript/src/shared/tools/documentation/githubReadme.ts @@ -0,0 +1,14 @@ +import fetch from 'node-fetch'; +import { ToolContext } from '@/shared/types'; + +export async function fetchGithubReadme( + repositoryName: string, + context: ToolContext +): Promise { + const url = `https://raw.githubusercontent.com/Mastercard/${repositoryName}/refs/heads/main/README.md`; + if (context.client.fetchGithubGuide !== undefined) { + return context.client.fetchGithubGuide(url).catch(() => undefined); + } + const response = await fetch(url); + return response.ok ? response.text() : undefined; +} From c836ffad7c6a30e1ddc2361bf8075e0753f9808a Mon Sep 17 00:00:00 2001 From: "Rocco, Marco" <7020500+ech0s7r@users.noreply.github.com> Date: Fri, 8 May 2026 13:57:41 -0400 Subject: [PATCH 4/4] Reformat long lines --- .../documentation/__tests__/githubReadme.test.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/typescript/src/shared/tools/documentation/__tests__/githubReadme.test.ts b/typescript/src/shared/tools/documentation/__tests__/githubReadme.test.ts index b179610..a2b0f53 100644 --- a/typescript/src/shared/tools/documentation/__tests__/githubReadme.test.ts +++ b/typescript/src/shared/tools/documentation/__tests__/githubReadme.test.ts @@ -26,7 +26,9 @@ describe('fetchGithubReadme', () => { }); it('returns undefined when fetchGithubGuide is provided but fails', async () => { - const fetchGithubGuide = jest.fn().mockRejectedValue(new Error('network error')); + const fetchGithubGuide = jest + .fn() + .mockRejectedValue(new Error('network error')); const result = await fetchGithubReadme('oauth1-signer-java', { client: { ...mockApi, fetchGithubGuide }, @@ -41,7 +43,9 @@ describe('fetchGithubReadme', () => { text: jest.fn().mockResolvedValue('github content'), } as any); - const result = await fetchGithubReadme('oauth1-signer-java', { client: mockApi }); + const result = await fetchGithubReadme('oauth1-signer-java', { + client: mockApi, + }); expect(mockFetch).toHaveBeenCalledWith( 'https://raw.githubusercontent.com/Mastercard/oauth1-signer-java/refs/heads/main/README.md' @@ -52,7 +56,9 @@ describe('fetchGithubReadme', () => { it('returns undefined when plain fetch response is not ok', async () => { mockFetch.mockResolvedValue({ ok: false } as any); - const result = await fetchGithubReadme('oauth1-signer-java', { client: mockApi }); + const result = await fetchGithubReadme('oauth1-signer-java', { + client: mockApi, + }); expect(result).toBeUndefined(); });