Skip to content

Commit 1ca26b3

Browse files
Julian De LucaJulian De Luca
authored andcommitted
fix: wire organization param through search URLs and auth helpers
1 parent 46c54bd commit 1ca26b3

File tree

6 files changed

+34
-31
lines changed

6 files changed

+34
-31
lines changed

src/tools/auth.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ interface IdentitiesResponse {
1010
value: IdentityBase[];
1111
}
1212

13-
async function getCurrentUserDetails(tokenProvider: () => Promise<string>, connectionProvider: () => Promise<WebApi>, userAgentProvider: () => string) {
14-
const connection = await getConnection(undefined, connectionProvider, tokenProvider, userAgentProvider);
13+
async function getCurrentUserDetails(tokenProvider: () => Promise<string>, connectionProvider: () => Promise<WebApi>, userAgentProvider: () => string, organization?: string) {
14+
const connection = await getConnection(organization, connectionProvider, tokenProvider, userAgentProvider);
1515
const url = `${connection.serverUrl}/_apis/connectionData`;
1616
const token = await tokenProvider();
1717
const response = await fetch(url, {
@@ -32,9 +32,9 @@ async function getCurrentUserDetails(tokenProvider: () => Promise<string>, conne
3232
/**
3333
* Searches for identities using Azure DevOps Identity API
3434
*/
35-
async function searchIdentities(identity: string, tokenProvider: () => Promise<string>, connectionProvider: () => Promise<WebApi>, userAgentProvider: () => string): Promise<IdentitiesResponse> {
35+
async function searchIdentities(identity: string, tokenProvider: () => Promise<string>, connectionProvider: () => Promise<WebApi>, userAgentProvider: () => string, organization?: string): Promise<IdentitiesResponse> {
3636
const token = await tokenProvider();
37-
const connection = await getConnection(undefined, connectionProvider, tokenProvider, userAgentProvider);
37+
const connection = await getConnection(organization, connectionProvider, tokenProvider, userAgentProvider);
3838
const orgName = connection.serverUrl.split("/")[3];
3939
const baseUrl = `https://vssps.dev.azure.com/${orgName}/_apis/identities`;
4040

@@ -63,8 +63,8 @@ async function searchIdentities(identity: string, tokenProvider: () => Promise<s
6363
/**
6464
* Gets the user ID from email or unique name using Azure DevOps Identity API
6565
*/
66-
async function getUserIdFromEmail(userEmail: string, tokenProvider: () => Promise<string>, connectionProvider: () => Promise<WebApi>, userAgentProvider: () => string): Promise<string> {
67-
const identities = await searchIdentities(userEmail, tokenProvider, connectionProvider, userAgentProvider);
66+
async function getUserIdFromEmail(userEmail: string, tokenProvider: () => Promise<string>, connectionProvider: () => Promise<WebApi>, userAgentProvider: () => string, organization?: string): Promise<string> {
67+
const identities = await searchIdentities(userEmail, tokenProvider, connectionProvider, userAgentProvider, organization);
6868

6969
if (!identities || identities.value?.length === 0) {
7070
throw new Error(`No user found with email/unique name: ${userEmail}`);

src/tools/core.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ function configureCoreTools(server: McpServer, tokenProvider: () => Promise<stri
139139
},
140140
async ({ searchFilter, organization }) => {
141141
try {
142-
const identities = await searchIdentities(searchFilter, tokenProvider, connectionProvider, userAgentProvider);
142+
const identities = await searchIdentities(searchFilter, tokenProvider, connectionProvider, userAgentProvider, organization);
143143

144144
if (!identities || identities.value?.length === 0) {
145145
return { content: [{ type: "text", text: "No identities found" }], isError: true };

src/tools/repositories.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ function configureRepoTools(server: McpServer, tokenProvider: () => Promise<stri
373373

374374
if (autoComplete !== undefined) {
375375
if (autoComplete) {
376-
const data = await getCurrentUserDetails(tokenProvider, connectionProvider, userAgentProvider);
376+
const data = await getCurrentUserDetails(tokenProvider, connectionProvider, userAgentProvider, organization);
377377
const autoCompleteUserId = data.authenticatedUser.id;
378378
updateRequest.autoCompleteSetBy = { id: autoCompleteUserId };
379379

@@ -609,7 +609,7 @@ function configureRepoTools(server: McpServer, tokenProvider: () => Promise<stri
609609

610610
if (created_by_user) {
611611
try {
612-
const userId = await getUserIdFromEmail(created_by_user, tokenProvider, connectionProvider, userAgentProvider);
612+
const userId = await getUserIdFromEmail(created_by_user, tokenProvider, connectionProvider, userAgentProvider, organization);
613613
searchCriteria.creatorId = userId;
614614
} catch (error) {
615615
return {
@@ -623,14 +623,14 @@ function configureRepoTools(server: McpServer, tokenProvider: () => Promise<stri
623623
};
624624
}
625625
} else if (created_by_me) {
626-
const data = await getCurrentUserDetails(tokenProvider, connectionProvider, userAgentProvider);
626+
const data = await getCurrentUserDetails(tokenProvider, connectionProvider, userAgentProvider, organization);
627627
const userId = data.authenticatedUser.id;
628628
searchCriteria.creatorId = userId;
629629
}
630630

631631
if (user_is_reviewer) {
632632
try {
633-
const reviewerUserId = await getUserIdFromEmail(user_is_reviewer, tokenProvider, connectionProvider, userAgentProvider);
633+
const reviewerUserId = await getUserIdFromEmail(user_is_reviewer, tokenProvider, connectionProvider, userAgentProvider, organization);
634634
searchCriteria.reviewerId = reviewerUserId;
635635
} catch (error) {
636636
return {
@@ -644,7 +644,7 @@ function configureRepoTools(server: McpServer, tokenProvider: () => Promise<stri
644644
};
645645
}
646646
} else if (i_am_reviewer) {
647-
const data = await getCurrentUserDetails(tokenProvider, connectionProvider, userAgentProvider);
647+
const data = await getCurrentUserDetails(tokenProvider, connectionProvider, userAgentProvider, organization);
648648
const userId = data.authenticatedUser.id;
649649
searchCriteria.reviewerId = userId;
650650
}
@@ -1510,7 +1510,7 @@ function configureRepoTools(server: McpServer, tokenProvider: () => Promise<stri
15101510
const connection = await getConnection(organization, connectionProvider, tokenProvider, userAgentProvider);
15111511
const gitApi = await connection.getGitApi();
15121512

1513-
const userDetails = await getCurrentUserDetails(tokenProvider, connectionProvider, userAgentProvider);
1513+
const userDetails = await getCurrentUserDetails(tokenProvider, connectionProvider, userAgentProvider, organization);
15141514
const userId = userDetails.authenticatedUser.id;
15151515

15161516
if (!userId) {

src/tools/search.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ function configureSearchTools(server: McpServer, tokenProvider: () => Promise<st
3535
async ({ searchText, project, repository, path, branch, includeFacets, skip, top, organization }) => {
3636
const accessToken = await tokenProvider();
3737
const connection = await getConnection(organization, connectionProvider, tokenProvider, userAgentProvider);
38-
const url = `https://almsearch.dev.azure.com/${orgName}/_apis/search/codesearchresults?api-version=${apiVersion}`;
38+
const resolvedOrg = organization ?? orgName;
39+
const url = `https://almsearch.dev.azure.com/${resolvedOrg}/_apis/search/codesearchresults?api-version=${apiVersion}`;
3940

4041
const requestBody: Record<string, unknown> = {
4142
searchText,
@@ -94,7 +95,8 @@ function configureSearchTools(server: McpServer, tokenProvider: () => Promise<st
9495
},
9596
async ({ searchText, project, wiki, includeFacets, skip, top, organization }) => {
9697
const accessToken = await tokenProvider();
97-
const url = `https://almsearch.dev.azure.com/${orgName}/_apis/search/wikisearchresults?api-version=${apiVersion}`;
98+
const resolvedOrg = organization ?? orgName;
99+
const url = `https://almsearch.dev.azure.com/${resolvedOrg}/_apis/search/wikisearchresults?api-version=${apiVersion}`;
98100

99101
const requestBody: Record<string, unknown> = {
100102
searchText,
@@ -149,7 +151,8 @@ function configureSearchTools(server: McpServer, tokenProvider: () => Promise<st
149151
},
150152
async ({ searchText, project, areaPath, workItemType, state, assignedTo, includeFacets, skip, top, organization }) => {
151153
const accessToken = await tokenProvider();
152-
const url = `https://almsearch.dev.azure.com/${orgName}/_apis/search/workitemsearchresults?api-version=${apiVersion}`;
154+
const resolvedOrg = organization ?? orgName;
155+
const url = `https://almsearch.dev.azure.com/${resolvedOrg}/_apis/search/workitemsearchresults?api-version=${apiVersion}`;
153156

154157
const requestBody: Record<string, unknown> = {
155158
searchText,

src/tools/wiki.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ function configureWikiTools(server: McpServer, tokenProvider: () => Promise<stri
255255
} else if (resp.status === 404) {
256256
return { content: [{ type: "text", text: `Error fetching wiki page content: Page with id ${parsed.pageId} not found` }], isError: true };
257257
}
258-
} catch {}
258+
} catch { }
259259
}
260260
}
261261

test/src/tools/repositories.test.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1690,7 +1690,7 @@ describe("repos tools", () => {
16901690

16911691
await handler(params);
16921692

1693-
expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("john@example.com", tokenProvider, connectionProvider, userAgentProvider);
1693+
expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("john@example.com", tokenProvider, connectionProvider, userAgentProvider, undefined);
16941694
expect(mockGitApi.getPullRequests).toHaveBeenCalledWith("repo123", { status: PullRequestStatus.Active, repositoryId: "repo123", creatorId: "specific-user-123" }, undefined, undefined, 0, 100);
16951695
});
16961696

@@ -1856,7 +1856,7 @@ describe("repos tools", () => {
18561856

18571857
await handler(params);
18581858

1859-
expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("reviewer@example.com", tokenProvider, connectionProvider, userAgentProvider);
1859+
expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("reviewer@example.com", tokenProvider, connectionProvider, userAgentProvider, undefined);
18601860
expect(mockGitApi.getPullRequests).toHaveBeenCalledWith("repo123", { status: PullRequestStatus.Active, repositoryId: "repo123", reviewerId: "reviewer-user-123" }, undefined, undefined, 0, 100);
18611861
});
18621862

@@ -1882,7 +1882,7 @@ describe("repos tools", () => {
18821882

18831883
await handler(params);
18841884

1885-
expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("specific-reviewer@example.com", tokenProvider, connectionProvider, userAgentProvider);
1885+
expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("specific-reviewer@example.com", tokenProvider, connectionProvider, userAgentProvider, undefined);
18861886
expect(mockGetCurrentUserDetails).not.toHaveBeenCalled(); // Should not be called since user_is_reviewer takes precedence
18871887
expect(mockGitApi.getPullRequests).toHaveBeenCalledWith(
18881888
"repo123",
@@ -1914,7 +1914,7 @@ describe("repos tools", () => {
19141914

19151915
const result = await handler(params);
19161916

1917-
expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("nonexistent@example.com", tokenProvider, connectionProvider, userAgentProvider);
1917+
expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("nonexistent@example.com", tokenProvider, connectionProvider, userAgentProvider, undefined);
19181918
expect(result.isError).toBe(true);
19191919
expect(result.content[0].text).toBe("Error finding reviewer with email nonexistent@example.com: User not found");
19201920
expect(mockGitApi.getPullRequests).not.toHaveBeenCalled();
@@ -2007,7 +2007,7 @@ describe("repos tools", () => {
20072007

20082008
const result = await handler(params);
20092009

2010-
expect(mockGetCurrentUserDetails).toHaveBeenCalledWith(tokenProvider, connectionProvider, userAgentProvider);
2010+
expect(mockGetCurrentUserDetails).toHaveBeenCalledWith(tokenProvider, connectionProvider, userAgentProvider, undefined);
20112011
expect(mockGitApi.getPullRequestsByProject).toHaveBeenCalledWith("test-project", { status: PullRequestStatus.Active, creatorId: "user123" }, undefined, 0, 100);
20122012

20132013
const expectedResult = [
@@ -2061,7 +2061,7 @@ describe("repos tools", () => {
20612061

20622062
const result = await handler(params);
20632063

2064-
expect(mockGetCurrentUserDetails).toHaveBeenCalledWith(tokenProvider, connectionProvider, userAgentProvider);
2064+
expect(mockGetCurrentUserDetails).toHaveBeenCalledWith(tokenProvider, connectionProvider, userAgentProvider, undefined);
20652065
expect(mockGitApi.getPullRequestsByProject).toHaveBeenCalledWith("test-project", { status: PullRequestStatus.Active, reviewerId: "user123" }, undefined, 0, 100);
20662066

20672067
const expectedResult = [
@@ -2116,7 +2116,7 @@ describe("repos tools", () => {
21162116

21172117
const result = await handler(params);
21182118

2119-
expect(mockGetCurrentUserDetails).toHaveBeenCalledWith(tokenProvider, connectionProvider, userAgentProvider);
2119+
expect(mockGetCurrentUserDetails).toHaveBeenCalledWith(tokenProvider, connectionProvider, userAgentProvider, undefined);
21202120
expect(mockGitApi.getPullRequestsByProject).toHaveBeenCalledWith("test-project", { status: PullRequestStatus.Active, creatorId: "user123", reviewerId: "user123" }, undefined, 0, 100);
21212121

21222122
const expectedResult = [
@@ -2174,7 +2174,7 @@ describe("repos tools", () => {
21742174

21752175
const result = await handler(params);
21762176

2177-
expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("specific@example.com", tokenProvider, connectionProvider, userAgentProvider);
2177+
expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("specific@example.com", tokenProvider, connectionProvider, userAgentProvider, undefined);
21782178
expect(mockGetCurrentUserDetails).not.toHaveBeenCalled(); // Should not be called when created_by_user is provided
21792179
expect(mockGitApi.getPullRequestsByProject).toHaveBeenCalledWith("test-project", { status: PullRequestStatus.Active, creatorId: "specific-user-123" }, undefined, 0, 100);
21802180

@@ -2362,7 +2362,7 @@ describe("repos tools", () => {
23622362

23632363
const result = await handler(params);
23642364

2365-
expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("reviewer@example.com", tokenProvider, connectionProvider, userAgentProvider);
2365+
expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("reviewer@example.com", tokenProvider, connectionProvider, userAgentProvider, undefined);
23662366
expect(mockGitApi.getPullRequestsByProject).toHaveBeenCalledWith("test-project", { status: PullRequestStatus.Active, reviewerId: "reviewer-user-123" }, undefined, 0, 100);
23672367

23682368
const expectedResult = [
@@ -2405,7 +2405,7 @@ describe("repos tools", () => {
24052405

24062406
await handler(params);
24072407

2408-
expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("specific-reviewer@example.com", tokenProvider, connectionProvider, userAgentProvider);
2408+
expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("specific-reviewer@example.com", tokenProvider, connectionProvider, userAgentProvider, undefined);
24092409
expect(mockGetCurrentUserDetails).not.toHaveBeenCalled(); // Should not be called since user_is_reviewer takes precedence
24102410
expect(mockGitApi.getPullRequestsByProject).toHaveBeenCalledWith("test-project", { status: PullRequestStatus.Active, reviewerId: "specific-reviewer-123" }, undefined, 0, 100);
24112411
});
@@ -2430,7 +2430,7 @@ describe("repos tools", () => {
24302430

24312431
const result = await handler(params);
24322432

2433-
expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("nonexistent@example.com", tokenProvider, connectionProvider, userAgentProvider);
2433+
expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("nonexistent@example.com", tokenProvider, connectionProvider, userAgentProvider, undefined);
24342434
expect(result.isError).toBe(true);
24352435
expect(result.content[0].text).toBe("Error finding reviewer with email nonexistent@example.com: User not found");
24362436
expect(mockGitApi.getPullRequestsByProject).not.toHaveBeenCalled();
@@ -2461,8 +2461,8 @@ describe("repos tools", () => {
24612461

24622462
await handler(params);
24632463

2464-
expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("creator@example.com", tokenProvider, connectionProvider, userAgentProvider);
2465-
expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("reviewer@example.com", tokenProvider, connectionProvider, userAgentProvider);
2464+
expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("creator@example.com", tokenProvider, connectionProvider, userAgentProvider, undefined);
2465+
expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("reviewer@example.com", tokenProvider, connectionProvider, userAgentProvider, undefined);
24662466
expect(mockGitApi.getPullRequestsByProject).toHaveBeenCalledWith(
24672467
"test-project",
24682468
{
@@ -4531,7 +4531,7 @@ describe("repos tools", () => {
45314531

45324532
const result = await handler(params);
45334533

4534-
expect(mockGetCurrentUserDetails).toHaveBeenCalledWith(tokenProvider, connectionProvider, userAgentProvider);
4534+
expect(mockGetCurrentUserDetails).toHaveBeenCalledWith(tokenProvider, connectionProvider, userAgentProvider, undefined);
45354535
expect(mockGitApi.createPullRequestReviewer).toHaveBeenCalledWith({ vote: 10, id: "user123" }, "repo123", 427, "user123");
45364536
expect(result.content[0].text).toBe("Successfully cast vote 'Approved' on PR #427.");
45374537
});

0 commit comments

Comments
 (0)