Repository: microsoft/azure-devops-mcp

Issue:
wiki_get_page_content does not support getting a wiki page by URL
It is useful for other sources that reference these wikis by url to download the document by URL. We should extend `wiki_get_page_content` to support getting a wiki page by URL.

Desire is to augment the function to have this functionality and parameters

```ts
    "Retrieve wiki page content. Provide either a 'url' parameter OR the combination of 'wikiIdentifier' and 'project' parameters.",
    {
      url: z.string().optional().describe("The full URL of the wiki page to retrieve content for. If provided, wikiIdentifier, project, and path are ignored."),
      wikiIdentifier: z.string().optional().describe("The unique identifier of the wiki. Required if url is not provided."),
      project: z.string().optional().describe("The project name or ID where the wiki is located. Required if url is not provided."),
      path: z.string().optional().describe("The path of the wiki page to retrieve content for. Optional, defaults to root page if not provided."),
    },
```

Hints from issue comments:
@danhellem: The WIKI Rest API's do not support this today. So unless I am missing something, I am not sure we can do so with MCP Server. If you have some ideas, please share and @mention me directly. But closing this issue for now.@KnicKnic: > The WIKI Rest API's do not support this today. So unless I am missing something, I am not sure we can do so with MCP Server. If you have some ideas, please share and @mention me directly. But closing this issue for now.

I think adding this feature is possible. I submitted a PR to do this operation. It has been quite useful feature for me as well as others. I would be happy to discuss.
@danhellem @grebeniuk: Looks like it can be done with https://learn.microsoft.com/en-us/rest/api/azure/devops/wiki/pages/get-page-by-id?view=azure-devops-rest-7.1&tabs=HTTP, as @KnicKnic demonstrated in the PR [wikiApi.getPageByIdText](https://github.com/microsoft/azure-devops-mcp/pull/171/files#diff-b17fb169ab8df3740ad297819df82b07c47565474ef0a50c23d3f63c42ed2da0R199)

@danhellem: @grebeniuk i will take a look. But we are being selective on new tools as we are getting a high tool count in the MCP Server@danhellem: @polatengin this might be a good one to look into. The PR #171 was created but I closed it at the time. I like the idea but it does seem like the parsing logic is more complicated than it needs to be. Could you take a look and maybe create a new PR and lets see how it goes?

Solution patch:
diff --git a/README.md b/README.md
index b32c822..fde8049 100644
--- a/README.md
+++ b/README.md
@@ -120,6 +120,7 @@ Interact with these Azure DevOps services:
 - **build_get_log**: Retrieve the logs for a specific build.
 - **build_get_log_by_id**: Get a specific build log by log ID.
 - **build_get_changes**: Get the changes associated with a specific build.
+- **build_get_timeline**: Retrieve the timeline for a specific build, showing detailed information about steps and tasks.
 - **build_run_build**: Trigger a new build for a specified definition.
 - **build_get_status**: Fetch the status of a specific build.
 - **build_update_build_stage**: Update the stage of a specific build.
diff --git a/docs/GETTINGSTARTED.md b/docs/GETTINGSTARTED.md
index d9d9069..fd25457 100644
--- a/docs/GETTINGSTARTED.md
+++ b/docs/GETTINGSTARTED.md
@@ -6,6 +6,7 @@ This guide will help you get started with the Azure DevOps MCP Server in differe
 - [Getting started with Visual Studio Code & GitHub Copilot](#️-visual-studio-code--github-copilot)
 - [Getting started with Visual Studio 2022 & GitHub Copilot](#-visual-studio-2022--github-copilot-1)
 - [Getting started with Claude Code](#-using-mcp-server-with-claude-code)
+- [Getting started with Claude Desktop](#️-using-mcp-server-with-claude-desktop)
 - [Getting started with Cursor](#-using-mcp-server-with-cursor)
 - [Optimizing Your Experience](#-optimizing-your-experience)
 
@@ -28,7 +29,7 @@ Before you begin, make sure you have:
 2. Install [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest)
 3. Open a project in Visual Studio
 
-### Azure Login
+### Azure CLI Login
 
 Ensure you are logged in to Azure DevOps via the Azure CLI:
 
@@ -216,6 +217,41 @@ claude mcp add azure-devops -- npx -y @azure-devops/mcp Contoso
 
 Replace `Contoso` with your own organization name
 
+### ✴️ Using MCP Server with Claude Desktop
+
+Ensure you are logged in to Azure DevOps using the Azure CLI:
+
+```sh
+az login
+```
+
+Open Claude Desktop and navigate to **File > Settings > Developer**. Click **Edit Config**.
+
+![Configuring MCP servers in Claude Desktop](../docs/media/claude-desktop-getting-started-1.png)
+
+Open the configuration file in your preferred editor (e.g., VS Code) and add the following JSON:
+
+```json
+{
+  "mcpServers": {
+    "ado": {
+      "command": "npx",
+      "args": ["-y", "@azure-devops/mcp", "{Contoso}"]
+    }
+  }
+}
+```
+
+Replace `{Contoso}` with your Azure DevOps organization name. Save the file and perform a hard restart of the Claude app.
+
+Start a new chat, then click the **Search and Tools** icon. The `ado` toolset should now be available.
+
+![ADO tools in Claude Desktop](../docs/media/claude-desktop-getting-started-2.png)
+
+You’re ready to start using the Azure DevOps MCP Server in Claude Desktop. Try a simple request such as: `get list of ado projects`.
+
+For additional guidance on Claude Desktop, see the [Quickstart](https://modelcontextprotocol.io/quickstart/user#installing-the-filesystem-server).
+
 ### 🍇 Using MCP Server with Cursor
 
 To integrate the Azure DevOps MCP Server with Cursor, create a `.cursor\mcp.json` file and add your Azure DevOps organization to the `mcpServers` list.
diff --git a/docs/media/claude-desktop-getting-started-1.png b/docs/media/claude-desktop-getting-started-1.png
new file mode 100644
index 0000000..3f99543
Binary files /dev/null and b/docs/media/claude-desktop-getting-started-1.png differ
diff --git a/docs/media/claude-desktop-getting-started-2.png b/docs/media/claude-desktop-getting-started-2.png
new file mode 100644
index 0000000..3eaa897
Binary files /dev/null and b/docs/media/claude-desktop-getting-started-2.png differ
diff --git a/src/tools/auth.ts b/src/tools/auth.ts
index a264679..13bffac 100644
--- a/src/tools/auth.ts
+++ b/src/tools/auth.ts
@@ -3,6 +3,12 @@
 
 import { AccessToken } from "@azure/identity";
 import { WebApi } from "azure-devops-node-api";
+import { apiVersion } from "../utils.js";
+import { IdentityBase } from "azure-devops-node-api/interfaces/IdentitiesInterfaces.js";
+
+interface IdentitiesResponse {
+  value: IdentityBase[];
+}
 
 async function getCurrentUserDetails(tokenProvider: () => Promise<AccessToken>, connectionProvider: () => Promise<WebApi>, userAgentProvider: () => string) {
   const connection = await connectionProvider();
@@ -23,4 +29,53 @@ async function getCurrentUserDetails(tokenProvider: () => Promise<AccessToken>,
   return data;
 }
 
-export { getCurrentUserDetails };
+/**
+ * Searches for identities using Azure DevOps Identity API
+ */
+async function searchIdentities(identity: string, tokenProvider: () => Promise<AccessToken>, connectionProvider: () => Promise<WebApi>, userAgentProvider: () => string): Promise<IdentitiesResponse> {
+  const token = await tokenProvider();
+  const connection = await connectionProvider();
+  const orgName = connection.serverUrl.split("/")[3];
+  const baseUrl = `https://vssps.dev.azure.com/${orgName}/_apis/identities`;
+
+  const params = new URLSearchParams({
+    "api-version": apiVersion,
+    "searchFilter": "General",
+    "filterValue": identity,
+  });
+
+  const response = await fetch(`${baseUrl}?${params}`, {
+    headers: {
+      "Authorization": `Bearer ${token.token}`,
+      "Content-Type": "application/json",
+      "User-Agent": userAgentProvider(),
+    },
+  });
+
+  if (!response.ok) {
+    const errorText = await response.text();
+    throw new Error(`HTTP ${response.status}: ${errorText}`);
+  }
+
+  return await response.json();
+}
+
+/**
+ * Gets the user ID from email or unique name using Azure DevOps Identity API
+ */
+async function getUserIdFromEmail(userEmail: string, tokenProvider: () => Promise<AccessToken>, connectionProvider: () => Promise<WebApi>, userAgentProvider: () => string): Promise<string> {
+  const identities = await searchIdentities(userEmail, tokenProvider, connectionProvider, userAgentProvider);
+
+  if (!identities || identities.value?.length === 0) {
+    throw new Error(`No user found with email/unique name: ${userEmail}`);
+  }
+
+  const firstIdentity = identities.value[0];
+  if (!firstIdentity.id) {
+    throw new Error(`No ID found for user with email/unique name: ${userEmail}`);
+  }
+
+  return firstIdentity.id;
+}
+
+export { getCurrentUserDetails, getUserIdFromEmail, searchIdentities };
diff --git a/src/tools/builds.ts b/src/tools/builds.ts
index 0ebec46..64a96f1 100644
--- a/src/tools/builds.ts
+++ b/src/tools/builds.ts
@@ -19,6 +19,7 @@ const BUILD_TOOLS = {
   run_build: "build_run_build",
   get_status: "build_get_status",
   update_build_stage: "build_update_build_stage",
+  get_timeline: "build_get_timeline",
 };
 
 function configureBuildTools(server: McpServer, tokenProvider: () => Promise<AccessToken>, connectionProvider: () => Promise<WebApi>, userAgentProvider: () => string) {
@@ -357,6 +358,27 @@ function configureBuildTools(server: McpServer, tokenProvider: () => Promise<Acc
       };
     }
   );
+
+  server.tool(
+    BUILD_TOOLS.get_timeline,
+    "Retrieves the timeline for a specific build, showing detailed information about steps and tasks.",
+    {
+      project: z.string().describe("Project ID or name to get the build timeline for"),
+      buildId: z.number().describe("ID of the build to get the timeline for"),
+      timelineId: z.string().optional().describe("The ID of a specific timeline to retrieve. If not specified, the primary timeline is returned."),
+      changeId: z.number().optional().describe("If specified, only includes timeline records that changed after this watermark."),
+      planId: z.string().optional().describe("The ID of the plan to retrieve the timeline for."),
+    },
+    async ({ project, buildId, timelineId, changeId, planId }) => {
+      const connection = await connectionProvider();
+      const buildApi = await connection.getBuildApi();
+      const timeline = await buildApi.getBuildTimeline(project, buildId, timelineId, changeId, planId);
+
+      return {
+        content: [{ type: "text", text: JSON.stringify(timeline, null, 2) }],
+      };
+    }
+  );
 }
 
 export { BUILD_TOOLS, configureBuildTools };
diff --git a/src/tools/core.ts b/src/tools/core.ts
index ae32343..5011d87 100644
--- a/src/tools/core.ts
+++ b/src/tools/core.ts
@@ -5,7 +5,7 @@ import { AccessToken } from "@azure/identity";
 import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
 import { WebApi } from "azure-devops-node-api";
 import { z } from "zod";
-import { apiVersion } from "../utils.js";
+import { searchIdentities } from "./auth.js";
 
 import type { ProjectInfo } from "azure-devops-node-api/interfaces/CoreInterfaces.js";
 import { IdentityBase } from "azure-devops-node-api/interfaces/IdentitiesInterfaces.js";
@@ -99,31 +99,7 @@ function configureCoreTools(server: McpServer, tokenProvider: () => Promise<Acce
     },
     async ({ searchFilter }) => {
       try {
-        const token = await tokenProvider();
-        const connection = await connectionProvider();
-        const orgName = connection.serverUrl.split("/")[3];
-        const baseUrl = `https://vssps.dev.azure.com/${orgName}/_apis/identities`;
-
-        const params = new URLSearchParams({
-          "api-version": apiVersion,
-          "searchFilter": "General",
-          "filterValue": searchFilter,
-        });
-
-        const response = await fetch(`${baseUrl}?${params}`, {
-          headers: {
-            "Authorization": `Bearer ${token.token}`,
-            "Content-Type": "application/json",
-            "User-Agent": userAgentProvider(),
-          },
-        });
-
-        if (!response.ok) {
-          const errorText = await response.text();
-          throw new Error(`HTTP ${response.status}: ${errorText}`);
-        }
-
-        const identities = await response.json();
+        const identities = await searchIdentities(searchFilter, tokenProvider, connectionProvider, userAgentProvider);
 
         if (!identities || identities.value?.length === 0) {
           return { content: [{ type: "text", text: "No identities found" }], isError: true };
diff --git a/src/tools/repos.ts b/src/tools/repos.ts
index 4a846c8..9a4c02d 100644
--- a/src/tools/repos.ts
+++ b/src/tools/repos.ts
@@ -18,7 +18,7 @@ import {
   CommentThreadStatus,
 } from "azure-devops-node-api/interfaces/GitInterfaces.js";
 import { z } from "zod";
-import { getCurrentUserDetails } from "./auth.js";
+import { getCurrentUserDetails, getUserIdFromEmail } from "./auth.js";
 import { GitRepository } from "azure-devops-node-api/interfaces/TfvcInterfaces.js";
 import { getEnumKeys } from "../utils.js";
 
@@ -270,13 +270,14 @@ function configureRepoTools(server: McpServer, tokenProvider: () => Promise<Acce
       top: z.number().default(100).describe("The maximum number of pull requests to return."),
       skip: z.number().default(0).describe("The number of pull requests to skip."),
       created_by_me: z.boolean().default(false).describe("Filter pull requests created by the current user."),
+      created_by_user: z.string().optional().describe("Filter pull requests created by a specific user (provide email or unique name). Takes precedence over created_by_me if both are provided."),
       i_am_reviewer: z.boolean().default(false).describe("Filter pull requests where the current user is a reviewer."),
       status: z
         .enum(getEnumKeys(PullRequestStatus) as [string, ...string[]])
         .default("Active")
         .describe("Filter pull requests by status. Defaults to 'Active'."),
     },
-    async ({ repositoryId, top, skip, created_by_me, i_am_reviewer, status }) => {
+    async ({ repositoryId, top, skip, created_by_me, created_by_user, i_am_reviewer, status }) => {
       const connection = await connectionProvider();
       const gitApi = await connection.getGitApi();
 
@@ -291,7 +292,22 @@ function configureRepoTools(server: McpServer, tokenProvider: () => Promise<Acce
         repositoryId: repositoryId,
       };
 
-      if (created_by_me || i_am_reviewer) {
+      if (created_by_user) {
+        try {
+          const userId = await getUserIdFromEmail(created_by_user, tokenProvider, connectionProvider, userAgentProvider);
+          searchCriteria.creatorId = userId;
+        } catch (error) {
+          return {
+            content: [
+              {
+                type: "text",
+                text: `Error finding user with email ${created_by_user}: ${error instanceof Error ? error.message : String(error)}`,
+              },
+            ],
+            isError: true,
+          };
+        }
+      } else if (created_by_me || i_am_reviewer) {
         const data = await getCurrentUserDetails(tokenProvider, connectionProvider, userAgentProvider);
         const userId = data.authenticatedUser.id;
         if (created_by_me) {
@@ -341,13 +357,14 @@ function configureRepoTools(server: McpServer, tokenProvider: () => Promise<Acce
       top: z.number().default(100).describe("The maximum number of pull requests to return."),
       skip: z.number().default(0).describe("The number of pull requests to skip."),
       created_by_me: z.boolean().default(false).describe("Filter pull requests created by the current user."),
+      created_by_user: z.string().optional().describe("Filter pull requests created by a specific user (provide email or unique name). Takes precedence over created_by_me if both are provided."),
       i_am_reviewer: z.boolean().default(false).describe("Filter pull requests where the current user is a reviewer."),
       status: z
         .enum(getEnumKeys(PullRequestStatus) as [string, ...string[]])
         .default("Active")
         .describe("Filter pull requests by status. Defaults to 'Active'."),
     },
-    async ({ project, top, skip, created_by_me, i_am_reviewer, status }) => {
+    async ({ project, top, skip, created_by_me, created_by_user, i_am_reviewer, status }) => {
       const connection = await connectionProvider();
       const gitApi = await connection.getGitApi();
 
@@ -360,7 +377,22 @@ function configureRepoTools(server: McpServer, tokenProvider: () => Promise<Acce
         status: pullRequestStatusStringToInt(status),
       };
 
-      if (created_by_me || i_am_reviewer) {
+      if (created_by_user) {
+        try {
+          const userId = await getUserIdFromEmail(created_by_user, tokenProvider, connectionProvider, userAgentProvider);
+          gitPullRequestSearchCriteria.creatorId = userId;
+        } catch (error) {
+          return {
+            content: [
+              {
+                type: "text",
+                text: `Error finding user with email ${created_by_user}: ${error instanceof Error ? error.message : String(error)}`,
+              },
+            ],
+            isError: true,
+          };
+        }
+      } else if (created_by_me || i_am_reviewer) {
         const data = await getCurrentUserDetails(tokenProvider, connectionProvider, userAgentProvider);
         const userId = data.authenticatedUser.id;
         if (created_by_me) {
diff --git a/src/tools/wiki.ts b/src/tools/wiki.ts
index 439172b..cdda04d 100644
--- a/src/tools/wiki.ts
+++ b/src/tools/wiki.ts
@@ -120,28 +120,86 @@ function configureWikiTools(server: McpServer, tokenProvider: () => Promise<Acce
 
   server.tool(
     WIKI_TOOLS.get_wiki_page_content,
-    "Retrieve wiki page content by wikiIdentifier and path.",
+    "Retrieve wiki page content. Provide either a 'url' parameter OR the combination of 'wikiIdentifier' and 'project' parameters.",
     {
-      wikiIdentifier: z.string().describe("The unique identifier of the wiki."),
-      project: z.string().describe("The project name or ID where the wiki is located."),
-      path: z.string().describe("The path of the wiki page to retrieve content for."),
+      url: z
+        .string()
+        .optional()
+        .describe(
+          "The full URL of the wiki page to retrieve content for. If provided, wikiIdentifier, project, and path are ignored. Supported patterns: https://dev.azure.com/{org}/{project}/_wiki/wikis/{wikiIdentifier}?pagePath=%2FMy%20Page and https://dev.azure.com/{org}/{project}/_wiki/wikis/{wikiIdentifier}/{pageId}/Page-Title"
+        ),
+      wikiIdentifier: z.string().optional().describe("The unique identifier of the wiki. Required if url is not provided."),
+      project: z.string().optional().describe("The project name or ID where the wiki is located. Required if url is not provided."),
+      path: z.string().optional().describe("The path of the wiki page to retrieve content for. Optional, defaults to root page if not provided."),
     },
-    async ({ wikiIdentifier, project, path }) => {
+    async ({ url, wikiIdentifier, project, path }: { url?: string; wikiIdentifier?: string; project?: string; path?: string }) => {
       try {
+        const hasUrl = !!url;
+        const hasPair = !!wikiIdentifier && !!project;
+        if (hasUrl && hasPair) {
+          return { content: [{ type: "text", text: "Error fetching wiki page content: Provide either 'url' OR 'wikiIdentifier' with 'project', not both." }], isError: true };
+        }
+        if (!hasUrl && !hasPair) {
+          return { content: [{ type: "text", text: "Error fetching wiki page content: You must provide either 'url' OR both 'wikiIdentifier' and 'project'." }], isError: true };
+        }
         const connection = await connectionProvider();
         const wikiApi = await connection.getWikiApi();
+        let resolvedProject = project;
+        let resolvedWiki = wikiIdentifier;
+        let resolvedPath: string | undefined = path;
+        let pageContent: string | undefined;
+
+        if (url) {
+          const parsed = parseWikiUrl(url);
+          if ("error" in parsed) {
+            return { content: [{ type: "text", text: `Error fetching wiki page content: ${parsed.error}` }], isError: true };
+          }
+          resolvedProject = parsed.project;
+          resolvedWiki = parsed.wikiIdentifier;
+          if (parsed.pagePath) {
+            resolvedPath = parsed.pagePath;
+          }
 
-        const stream = await wikiApi.getPageText(project, wikiIdentifier, path, undefined, undefined, true);
-
-        if (!stream) {
-          return { content: [{ type: "text", text: "No wiki page content found" }], isError: true };
+          if (parsed.pageId) {
+            try {
+              let accessToken: AccessToken | undefined;
+              try {
+                accessToken = await tokenProvider();
+              } catch {}
+              const baseUrl = connection.serverUrl.replace(/\/$/, "");
+              const restUrl = `${baseUrl}/${resolvedProject}/_apis/wiki/wikis/${resolvedWiki}/pages/${parsed.pageId}?includeContent=true&api-version=7.1`;
+              const resp = await fetch(restUrl, {
+                headers: accessToken?.token ? { Authorization: `Bearer ${accessToken.token}` } : {},
+              });
+              if (resp.ok) {
+                const json = await resp.json();
+                if (json && typeof json.content === "string") {
+                  pageContent = json.content;
+                } else if (json && json.path) {
+                  resolvedPath = json.path;
+                }
+              } else if (resp.status === 404) {
+                return { content: [{ type: "text", text: `Error fetching wiki page content: Page with id ${parsed.pageId} not found` }], isError: true };
+              }
+            } catch {}
+          }
         }
 
-        const content = await streamToString(stream);
+        if (!pageContent) {
+          if (!resolvedPath) {
+            resolvedPath = "/";
+          }
+          if (!resolvedProject || !resolvedWiki) {
+            return { content: [{ type: "text", text: "Project and wikiIdentifier must be defined to fetch wiki page content." }], isError: true };
+          }
+          const stream = await wikiApi.getPageText(resolvedProject, resolvedWiki, resolvedPath, undefined, undefined, true);
+          if (!stream) {
+            return { content: [{ type: "text", text: "No wiki page content found" }], isError: true };
+          }
+          pageContent = await streamToString(stream);
+        }
 
-        return {
-          content: [{ type: "text", text: JSON.stringify(content, null, 2) }],
-        };
+        return { content: [{ type: "text", text: JSON.stringify(pageContent, null, 2) }] };
       } catch (error) {
         const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
 
@@ -281,4 +339,49 @@ function streamToString(stream: NodeJS.ReadableStream): Promise<string> {
   });
 }
 
+// Helper to parse Azure DevOps wiki page URLs.
+// Supported examples:
+//  - https://dev.azure.com/org/project/_wiki/wikis/wikiIdentifier?wikiVersion=GBmain&pagePath=%2FHome
+//  - https://dev.azure.com/org/project/_wiki/wikis/wikiIdentifier/123/Title-Of-Page
+// Returns either a structured object OR an error message inside { error }.
+function parseWikiUrl(url: string): { project: string; wikiIdentifier: string; pagePath?: string; pageId?: number; error?: undefined } | { error: string } {
+  try {
+    const u = new URL(url);
+    // Path segments after host
+    // Expect pattern: /{project}/_wiki/wikis/{wikiIdentifier}[/{pageId}/...]
+    const segments = u.pathname.split("/").filter(Boolean); // remove empty
+    const idx = segments.findIndex((s) => s === "_wiki");
+    if (idx < 1 || segments[idx + 1] !== "wikis") {
+      return { error: "URL does not match expected wiki pattern (missing /_wiki/wikis/ segment)." };
+    }
+    const project = segments[idx - 1];
+    const wikiIdentifier = segments[idx + 2];
+    if (!project || !wikiIdentifier) {
+      return { error: "Could not extract project or wikiIdentifier from URL." };
+    }
+
+    // Query form with pagePath
+    const pagePathParam = u.searchParams.get("pagePath");
+    if (pagePathParam) {
+      let decoded = decodeURIComponent(pagePathParam);
+      if (!decoded.startsWith("/")) decoded = "/" + decoded;
+      return { project, wikiIdentifier, pagePath: decoded };
+    }
+
+    // Path ID form: .../wikis/{wikiIdentifier}/{pageId}/...
+    const afterWiki = segments.slice(idx + 3); // elements after wikiIdentifier
+    if (afterWiki.length >= 1) {
+      const maybeId = parseInt(afterWiki[0], 10);
+      if (!isNaN(maybeId)) {
+        return { project, wikiIdentifier, pageId: maybeId };
+      }
+    }
+
+    // If nothing else specified, treat as root page
+    return { project, wikiIdentifier, pagePath: "/" };
+  } catch {
+    return { error: "Invalid URL format." };
+  }
+}
+
 export { WIKI_TOOLS, configureWikiTools };



Test patch:
diff --git a/test/src/tools/advsec.test.ts b/test/src/tools/advsec.test.ts
index ce105f9..7b3315a 100644
--- a/test/src/tools/advsec.test.ts
+++ b/test/src/tools/advsec.test.ts
@@ -438,6 +438,198 @@ describe("configureAdvSecTools", () => {
         undefined // continuationToken
       );
     });
+
+    it("should handle optional parameters correctly when not provided", async () => {
+      configureAdvSecTools(server, tokenProvider, connectionProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "advsec_get_alerts");
+      if (!call) throw new Error("advsec_get_alerts tool not registered");
+      const [, , , handler] = call;
+
+      const mockResult: PagedList<Alert> = [];
+      (mockAlertApi.getAlerts as jest.Mock).mockResolvedValue(mockResult);
+
+      // Test with minimal parameters - only required ones
+      const minimalParams = {
+        project: "test-project",
+        repository: "test-repo",
+      };
+
+      await handler(minimalParams);
+
+      // When optional parameters aren't provided, they remain undefined
+      expect(mockAlertApi.getAlerts).toHaveBeenLastCalledWith(
+        "test-project",
+        "test-repo",
+        undefined, // top (optional, no default applied by handler)
+        undefined, // orderBy (optional, no default applied by handler)
+        {}, // empty criteria object since no optional filters provided
+        undefined, // expand
+        undefined // continuationToken
+      );
+    });
+
+    it("should include all optional parameters when provided", async () => {
+      configureAdvSecTools(server, tokenProvider, connectionProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "advsec_get_alerts");
+      if (!call) throw new Error("advsec_get_alerts tool not registered");
+      const [, , , handler] = call;
+
+      const mockResult: PagedList<Alert> = [];
+      (mockAlertApi.getAlerts as jest.Mock).mockResolvedValue(mockResult);
+
+      // Test with all optional parameters provided
+      const allParamsParams = {
+        project: "test-project",
+        repository: "test-repo",
+        alertType: "dependency",
+        states: ["active", "dismissed"],
+        severities: ["high", "medium"],
+        ruleId: "rule123",
+        ruleName: "security-rule",
+        toolName: "CodeQL",
+        ref: "refs/heads/main",
+        onlyDefaultBranch: false,
+        top: 50,
+        orderBy: "id",
+        continuationToken: "token123",
+      };
+
+      await handler(allParamsParams);
+
+      expect(mockAlertApi.getAlerts).toHaveBeenLastCalledWith(
+        "test-project",
+        "test-repo",
+        50, // top
+        "id", // orderBy
+        expect.objectContaining({
+          alertType: AlertType.Dependency,
+          states: [State.Active, State.Dismissed],
+          severities: [Severity.High, Severity.Medium],
+          ruleId: "rule123",
+          ruleName: "security-rule",
+          toolName: "CodeQL",
+          ref: "refs/heads/main",
+          onlyDefaultBranch: false,
+        }),
+        undefined, // expand
+        "token123" // continuationToken
+      );
+
+      // Verify all optional fields are included
+      const lastCall = (mockAlertApi.getAlerts as jest.Mock).mock.calls[0];
+      const criteria = lastCall[4];
+      expect(criteria).toHaveProperty("alertType", AlertType.Dependency);
+      expect(criteria).toHaveProperty("states", [State.Active, State.Dismissed]);
+      expect(criteria).toHaveProperty("severities", [Severity.High, Severity.Medium]);
+      expect(criteria).toHaveProperty("ruleId", "rule123");
+      expect(criteria).toHaveProperty("ruleName", "security-rule");
+      expect(criteria).toHaveProperty("toolName", "CodeQL");
+      expect(criteria).toHaveProperty("ref", "refs/heads/main");
+      expect(criteria).toHaveProperty("onlyDefaultBranch", false);
+    });
+
+    it("should handle onlyDefaultBranch parameter correctly", async () => {
+      configureAdvSecTools(server, tokenProvider, connectionProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "advsec_get_alerts");
+      if (!call) throw new Error("advsec_get_alerts tool not registered");
+      const [, , , handler] = call;
+
+      const mockResult: PagedList<Alert> = [];
+      (mockAlertApi.getAlerts as jest.Mock).mockResolvedValue(mockResult);
+
+      // Test with onlyDefaultBranch explicitly set to false
+      const falseParams = {
+        project: "test-project",
+        repository: "test-repo",
+        onlyDefaultBranch: false,
+      };
+
+      await handler(falseParams);
+
+      expect(mockAlertApi.getAlerts).toHaveBeenLastCalledWith(
+        "test-project",
+        "test-repo",
+        undefined, // top (not provided)
+        undefined, // orderBy (not provided)
+        expect.objectContaining({
+          onlyDefaultBranch: false,
+        }),
+        undefined, // expand
+        undefined // continuationToken
+      );
+
+      // Test with onlyDefaultBranch explicitly set to true
+      const trueParams = {
+        project: "test-project",
+        repository: "test-repo",
+        onlyDefaultBranch: true,
+      };
+
+      await handler(trueParams);
+
+      expect(mockAlertApi.getAlerts).toHaveBeenLastCalledWith(
+        "test-project",
+        "test-repo",
+        undefined, // top (not provided)
+        undefined, // orderBy (not provided)
+        expect.objectContaining({
+          onlyDefaultBranch: true,
+        }),
+        undefined, // expand
+        undefined // continuationToken
+      );
+    });
+
+    it("should handle secret alerts without confidenceLevels or validity", async () => {
+      configureAdvSecTools(server, tokenProvider, connectionProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "advsec_get_alerts");
+      if (!call) throw new Error("advsec_get_alerts tool not registered");
+      const [, , , handler] = call;
+
+      const mockResult: PagedList<Alert> = [];
+      (mockAlertApi.getAlerts as jest.Mock).mockResolvedValue(mockResult);
+
+      // Test secret alert without confidenceLevels or validity explicitly provided
+      const secretWithoutParams = {
+        project: "test-project",
+        repository: "test-repo",
+        alertType: "secret",
+      };
+
+      await handler(secretWithoutParams);
+
+      const lastCall = (mockAlertApi.getAlerts as jest.Mock).mock.calls[0];
+      const criteria = lastCall[4];
+      expect(criteria).toHaveProperty("alertType", AlertType.Secret);
+      // confidenceLevels and validity should not be included if not explicitly provided
+      expect(criteria).not.toHaveProperty("confidenceLevels");
+      expect(criteria).not.toHaveProperty("validity");
+    });
+
+    it("should handle non-Error exception types", async () => {
+      configureAdvSecTools(server, tokenProvider, connectionProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "advsec_get_alerts");
+      if (!call) throw new Error("advsec_get_alerts tool not registered");
+      const [, , , handler] = call;
+
+      // Test with non-Error exception (string)
+      (mockAlertApi.getAlerts as jest.Mock).mockRejectedValue("String error");
+
+      const params = {
+        project: "test-project",
+        repository: "test-repo",
+      };
+
+      const result = await handler(params);
+
+      expect(result.isError).toBe(true);
+      expect(result.content[0].text).toContain("Error fetching Advanced Security alerts: Unknown error occurred");
+    });
   });
 
   describe("advsec_get_alert_details tool", () => {
@@ -487,6 +679,44 @@ describe("configureAdvSecTools", () => {
       expect(result.isError).toBeUndefined();
     });
 
+    it("should fetch specific alert details with ref parameter", async () => {
+      configureAdvSecTools(server, tokenProvider, connectionProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "advsec_get_alert_details");
+      if (!call) throw new Error("advsec_get_alert_details tool not registered");
+      const [, , , handler] = call;
+
+      const mockResult: Alert = {
+        alertId: 1,
+        state: State.Active,
+        severity: Severity.High,
+        alertType: AlertType.Code,
+        title: "Test security alert",
+      };
+
+      (mockAlertApi.getAlert as jest.Mock).mockResolvedValue(mockResult);
+
+      const params = {
+        project: "test-project",
+        repository: "test-repo",
+        alertId: 1,
+        ref: "refs/heads/feature-branch",
+      };
+
+      const result = await handler(params);
+
+      expect(mockAlertApi.getAlert).toHaveBeenCalledWith(
+        "test-project",
+        1,
+        "test-repo",
+        "refs/heads/feature-branch", // ref
+        undefined // expand
+      );
+
+      expect(result.content[0].text).toBe(JSON.stringify(mockResult, null, 2));
+      expect(result.isError).toBeUndefined();
+    });
+
     it("should handle API errors correctly", async () => {
       configureAdvSecTools(server, tokenProvider, connectionProvider);
 
@@ -510,6 +740,28 @@ describe("configureAdvSecTools", () => {
       expect(result.content[0].text).toContain("Error fetching alert details: Alert not found");
     });
 
+    it("should handle non-Error exception types", async () => {
+      configureAdvSecTools(server, tokenProvider, connectionProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "advsec_get_alert_details");
+      if (!call) throw new Error("advsec_get_alert_details tool not registered");
+      const [, , , handler] = call;
+
+      // Test with non-Error exception (string)
+      (mockAlertApi.getAlert as jest.Mock).mockRejectedValue("String error");
+
+      const params = {
+        project: "test-project",
+        repository: "test-repo",
+        alertId: 999,
+      };
+
+      const result = await handler(params);
+
+      expect(result.isError).toBe(true);
+      expect(result.content[0].text).toContain("Error fetching alert details: Unknown error occurred");
+    });
+
     it("should handle null API results correctly", async () => {
       configureAdvSecTools(server, tokenProvider, connectionProvider);
 
diff --git a/test/src/tools/auth.test.ts b/test/src/tools/auth.test.ts
new file mode 100644
index 0000000..b10db71
--- /dev/null
+++ b/test/src/tools/auth.test.ts
@@ -0,0 +1,321 @@
+import { AccessToken } from "@azure/identity";
+import { describe, expect, it, beforeEach, afterEach } from "@jest/globals";
+import { WebApi } from "azure-devops-node-api";
+import { getCurrentUserDetails, getUserIdFromEmail, searchIdentities } from "../../../src/tools/auth";
+
+type TokenProviderMock = () => Promise<AccessToken>;
+type ConnectionProviderMock = () => Promise<WebApi>;
+
+describe("auth functions", () => {
+  let tokenProvider: TokenProviderMock;
+  let connectionProvider: ConnectionProviderMock;
+  let userAgentProvider: () => string;
+  let mockConnection: WebApi;
+
+  beforeEach(() => {
+    tokenProvider = jest.fn();
+    userAgentProvider = () => "Jest";
+
+    mockConnection = {
+      serverUrl: "https://dev.azure.com/test-org",
+    } as WebApi;
+
+    connectionProvider = jest.fn().mockResolvedValue(mockConnection);
+
+    // Mock fetch globally for these tests
+    global.fetch = jest.fn();
+  });
+
+  afterEach(() => {
+    jest.restoreAllMocks();
+  });
+
+  describe("getCurrentUserDetails", () => {
+    it("should fetch current user details with correct parameters", async () => {
+      // Mock token provider
+      (tokenProvider as jest.Mock).mockResolvedValue({ token: "fake-token" });
+
+      // Mock fetch response
+      const mockUserData = {
+        authenticatedUser: {
+          id: "user-123",
+          displayName: "Test User",
+          uniqueName: "test@example.com",
+        },
+      };
+
+      (global.fetch as jest.Mock).mockResolvedValue({
+        ok: true,
+        json: jest.fn().mockResolvedValue(mockUserData),
+      });
+
+      const result = await getCurrentUserDetails(tokenProvider, connectionProvider, userAgentProvider);
+
+      expect(global.fetch).toHaveBeenCalledWith("https://dev.azure.com/test-org/_apis/connectionData", {
+        method: "GET",
+        headers: {
+          "Authorization": "Bearer fake-token",
+          "Content-Type": "application/json",
+          "User-Agent": "Jest",
+        },
+      });
+
+      expect(result).toEqual(mockUserData);
+    });
+
+    it("should handle HTTP error responses correctly", async () => {
+      (tokenProvider as jest.Mock).mockResolvedValue({ token: "fake-token" });
+
+      const errorData = { message: "Unauthorized" };
+      (global.fetch as jest.Mock).mockResolvedValue({
+        ok: false,
+        status: 401,
+        json: jest.fn().mockResolvedValue(errorData),
+      });
+
+      await expect(getCurrentUserDetails(tokenProvider, connectionProvider, userAgentProvider)).rejects.toThrow("Error fetching user details: Unauthorized");
+    });
+
+    it("should handle network errors correctly", async () => {
+      (tokenProvider as jest.Mock).mockResolvedValue({ token: "fake-token" });
+
+      (global.fetch as jest.Mock).mockRejectedValue(new Error("Network error"));
+
+      await expect(getCurrentUserDetails(tokenProvider, connectionProvider, userAgentProvider)).rejects.toThrow("Network error");
+    });
+  });
+
+  describe("searchIdentities", () => {
+    it("should search identities with correct parameters and return expected result", async () => {
+      // Mock token provider
+      (tokenProvider as jest.Mock).mockResolvedValue({ token: "fake-token" });
+
+      // Mock fetch response
+      const mockIdentities = {
+        value: [
+          {
+            id: "user1-id",
+            providerDisplayName: "John Doe",
+            descriptor: "aad.user1-descriptor",
+          },
+          {
+            id: "user2-id",
+            providerDisplayName: "Jane Smith",
+            descriptor: "aad.user2-descriptor",
+          },
+        ],
+      };
+
+      (global.fetch as jest.Mock).mockResolvedValue({
+        ok: true,
+        json: jest.fn().mockResolvedValue(mockIdentities),
+      });
+
+      const result = await searchIdentities("john.doe@example.com", tokenProvider, connectionProvider, userAgentProvider);
+
+      expect(global.fetch).toHaveBeenCalledWith("https://vssps.dev.azure.com/test-org/_apis/identities?api-version=7.2-preview.1&searchFilter=General&filterValue=john.doe%40example.com", {
+        headers: {
+          "Authorization": "Bearer fake-token",
+          "Content-Type": "application/json",
+          "User-Agent": "Jest",
+        },
+      });
+
+      expect(result).toEqual(mockIdentities);
+    });
+
+    it("should handle HTTP error responses correctly", async () => {
+      (tokenProvider as jest.Mock).mockResolvedValue({ token: "fake-token" });
+
+      // Mock failed HTTP response
+      (global.fetch as jest.Mock).mockResolvedValue({
+        ok: false,
+        status: 404,
+        text: jest.fn().mockResolvedValue("Not Found"),
+      });
+
+      await expect(searchIdentities("nonexistent@example.com", tokenProvider, connectionProvider, userAgentProvider)).rejects.toThrow("HTTP 404: Not Found");
+    });
+
+    it("should handle network errors correctly", async () => {
+      (tokenProvider as jest.Mock).mockResolvedValue({ token: "fake-token" });
+
+      (global.fetch as jest.Mock).mockRejectedValue(new Error("Network timeout"));
+
+      await expect(searchIdentities("test@example.com", tokenProvider, connectionProvider, userAgentProvider)).rejects.toThrow("Network timeout");
+    });
+
+    it("should properly encode search filter in URL", async () => {
+      (tokenProvider as jest.Mock).mockResolvedValue({ token: "fake-token" });
+
+      (global.fetch as jest.Mock).mockResolvedValue({
+        ok: true,
+        json: jest.fn().mockResolvedValue({ value: [] }),
+      });
+
+      await searchIdentities("user with spaces@example.com", tokenProvider, connectionProvider, userAgentProvider);
+
+      expect(global.fetch).toHaveBeenCalledWith(
+        "https://vssps.dev.azure.com/test-org/_apis/identities?api-version=7.2-preview.1&searchFilter=General&filterValue=user+with+spaces%40example.com",
+        expect.any(Object)
+      );
+    });
+  });
+
+  describe("getUserIdFromEmail", () => {
+    it("should return user ID from email with correct parameters", async () => {
+      // Mock token provider
+      (tokenProvider as jest.Mock).mockResolvedValue({ token: "fake-token" });
+
+      // Mock fetch response with single user
+      const mockIdentities = {
+        value: [
+          {
+            id: "user1-id",
+            providerDisplayName: "John Doe",
+            descriptor: "aad.user1-descriptor",
+          },
+        ],
+      };
+
+      (global.fetch as jest.Mock).mockResolvedValue({
+        ok: true,
+        json: jest.fn().mockResolvedValue(mockIdentities),
+      });
+
+      const result = await getUserIdFromEmail("john.doe@example.com", tokenProvider, connectionProvider, userAgentProvider);
+
+      expect(global.fetch).toHaveBeenCalledWith("https://vssps.dev.azure.com/test-org/_apis/identities?api-version=7.2-preview.1&searchFilter=General&filterValue=john.doe%40example.com", {
+        headers: {
+          "Authorization": "Bearer fake-token",
+          "Content-Type": "application/json",
+          "User-Agent": "Jest",
+        },
+      });
+
+      expect(result).toBe("user1-id");
+    });
+
+    it("should return first user ID when multiple users found", async () => {
+      (tokenProvider as jest.Mock).mockResolvedValue({ token: "fake-token" });
+
+      const mockIdentities = {
+        value: [
+          {
+            id: "user1-id",
+            providerDisplayName: "John Doe",
+            descriptor: "aad.user1-descriptor",
+          },
+          {
+            id: "user2-id",
+            providerDisplayName: "Johnny Doe",
+            descriptor: "aad.user2-descriptor",
+          },
+        ],
+      };
+
+      (global.fetch as jest.Mock).mockResolvedValue({
+        ok: true,
+        json: jest.fn().mockResolvedValue(mockIdentities),
+      });
+
+      const result = await getUserIdFromEmail("john.doe@example.com", tokenProvider, connectionProvider, userAgentProvider);
+
+      expect(result).toBe("user1-id");
+    });
+
+    it("should throw error when no users found", async () => {
+      (tokenProvider as jest.Mock).mockResolvedValue({ token: "fake-token" });
+
+      // Mock empty response
+      (global.fetch as jest.Mock).mockResolvedValue({
+        ok: true,
+        json: jest.fn().mockResolvedValue({ value: [] }),
+      });
+
+      await expect(getUserIdFromEmail("nobody@example.com", tokenProvider, connectionProvider, userAgentProvider)).rejects.toThrow("No user found with email/unique name: nobody@example.com");
+    });
+
+    it("should throw error when null response", async () => {
+      (tokenProvider as jest.Mock).mockResolvedValue({ token: "fake-token" });
+
+      // Mock null response
+      (global.fetch as jest.Mock).mockResolvedValue({
+        ok: true,
+        json: jest.fn().mockResolvedValue(null),
+      });
+
+      await expect(getUserIdFromEmail("test@example.com", tokenProvider, connectionProvider, userAgentProvider)).rejects.toThrow("No user found with email/unique name: test@example.com");
+    });
+
+    it("should throw error when user has no ID", async () => {
+      (tokenProvider as jest.Mock).mockResolvedValue({ token: "fake-token" });
+
+      // Mock response with user without ID
+      const mockIdentities = {
+        value: [
+          {
+            id: undefined,
+            providerDisplayName: "John Doe",
+            descriptor: "aad.user1-descriptor",
+          },
+        ],
+      };
+
+      (global.fetch as jest.Mock).mockResolvedValue({
+        ok: true,
+        json: jest.fn().mockResolvedValue(mockIdentities),
+      });
+
+      await expect(getUserIdFromEmail("john.doe@example.com", tokenProvider, connectionProvider, userAgentProvider)).rejects.toThrow(
+        "No ID found for user with email/unique name: john.doe@example.com"
+      );
+    });
+
+    it("should handle HTTP error responses correctly", async () => {
+      (tokenProvider as jest.Mock).mockResolvedValue({ token: "fake-token" });
+
+      // Mock failed HTTP response
+      (global.fetch as jest.Mock).mockResolvedValue({
+        ok: false,
+        status: 403,
+        text: jest.fn().mockResolvedValue("Forbidden"),
+      });
+
+      await expect(getUserIdFromEmail("test@example.com", tokenProvider, connectionProvider, userAgentProvider)).rejects.toThrow("HTTP 403: Forbidden");
+    });
+
+    it("should handle network errors correctly", async () => {
+      (tokenProvider as jest.Mock).mockResolvedValue({ token: "fake-token" });
+
+      (global.fetch as jest.Mock).mockRejectedValue(new Error("Connection refused"));
+
+      await expect(getUserIdFromEmail("test@example.com", tokenProvider, connectionProvider, userAgentProvider)).rejects.toThrow("Connection refused");
+    });
+
+    it("should work with unique names as well as emails", async () => {
+      (tokenProvider as jest.Mock).mockResolvedValue({ token: "fake-token" });
+
+      const mockIdentities = {
+        value: [
+          {
+            id: "user1-id",
+            providerDisplayName: "John Doe",
+            descriptor: "aad.user1-descriptor",
+          },
+        ],
+      };
+
+      (global.fetch as jest.Mock).mockResolvedValue({
+        ok: true,
+        json: jest.fn().mockResolvedValue(mockIdentities),
+      });
+
+      const result = await getUserIdFromEmail("john.doe", tokenProvider, connectionProvider, userAgentProvider);
+
+      expect(global.fetch).toHaveBeenCalledWith("https://vssps.dev.azure.com/test-org/_apis/identities?api-version=7.2-preview.1&searchFilter=General&filterValue=john.doe", expect.any(Object));
+
+      expect(result).toBe("user1-id");
+    });
+  });
+});
diff --git a/test/src/tools/builds.test.ts b/test/src/tools/builds.test.ts
index 216d723..defa664 100644
--- a/test/src/tools/builds.test.ts
+++ b/test/src/tools/builds.test.ts
@@ -871,4 +871,104 @@ describe("configureBuildTools", () => {
       await expect(handler(params)).rejects.toThrow("Build not found");
     });
   });
+
+  describe("get_timeline tool", () => {
+    it("should retrieve the timeline for a build", async () => {
+      configureBuildTools(server, tokenProvider, connectionProvider, userAgentProvider);
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "build_get_timeline");
+      if (!call) throw new Error("build_get_timeline tool not registered");
+      const [, , , handler] = call;
+
+      const mockTimeline = {
+        id: "timeline-123",
+        records: [
+          {
+            id: "record-1",
+            name: "Initialize job",
+            type: "Task",
+            startTime: "2024-08-19T10:00:00.000Z",
+            finishTime: "2024-08-19T10:01:00.000Z",
+            state: "Completed",
+            result: "Succeeded",
+          },
+          {
+            id: "record-2",
+            name: "Checkout",
+            type: "Task",
+            startTime: "2024-08-19T10:01:00.000Z",
+            finishTime: "2024-08-19T10:02:00.000Z",
+            state: "Completed",
+            result: "Succeeded",
+          },
+        ],
+      };
+
+      const mockBuildApi = {
+        getBuildTimeline: jest.fn().mockResolvedValue(mockTimeline),
+      };
+      mockConnection.getBuildApi.mockResolvedValue(mockBuildApi);
+
+      const params = {
+        project: "test-project",
+        buildId: 123,
+        timelineId: "timeline-123",
+        changeId: 456,
+        planId: "plan-789",
+      };
+
+      const result = await handler(params);
+
+      expect(mockBuildApi.getBuildTimeline).toHaveBeenCalledWith("test-project", 123, "timeline-123", 456, "plan-789");
+      expect(result.content[0].text).toBe(JSON.stringify(mockTimeline, null, 2));
+    });
+
+    it("should handle minimal parameters for get_timeline", async () => {
+      configureBuildTools(server, tokenProvider, connectionProvider, userAgentProvider);
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "build_get_timeline");
+      if (!call) throw new Error("build_get_timeline tool not registered");
+      const [, , , handler] = call;
+
+      const mockTimeline = {
+        id: "timeline-123",
+        records: [
+          { id: "record-1", name: "Task 1", state: "Completed" },
+          { id: "record-2", name: "Task 2", state: "Completed" },
+        ],
+      };
+
+      const mockBuildApi = {
+        getBuildTimeline: jest.fn().mockResolvedValue(mockTimeline),
+      };
+      mockConnection.getBuildApi.mockResolvedValue(mockBuildApi);
+
+      const params = {
+        project: "test-project",
+        buildId: 123,
+      };
+
+      const result = await handler(params);
+
+      expect(mockBuildApi.getBuildTimeline).toHaveBeenCalledWith("test-project", 123, undefined, undefined, undefined);
+      expect(result.content[0].text).toBe(JSON.stringify(mockTimeline, null, 2));
+    });
+
+    it("should handle API errors for get_timeline", async () => {
+      configureBuildTools(server, tokenProvider, connectionProvider, userAgentProvider);
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "build_get_timeline");
+      if (!call) throw new Error("build_get_timeline tool not registered");
+      const [, , , handler] = call;
+
+      const mockBuildApi = {
+        getBuildTimeline: jest.fn().mockRejectedValue(new Error("Timeline not found")),
+      };
+      mockConnection.getBuildApi.mockResolvedValue(mockBuildApi);
+
+      const params = {
+        project: "test-project",
+        buildId: 999,
+      };
+
+      await expect(handler(params)).rejects.toThrow("Timeline not found");
+    });
+  });
 });
diff --git a/test/src/tools/repos.test.ts b/test/src/tools/repos.test.ts
index d90f247..984fced 100644
--- a/test/src/tools/repos.test.ts
+++ b/test/src/tools/repos.test.ts
@@ -6,14 +6,16 @@ import { AccessToken } from "@azure/identity";
 import { WebApi } from "azure-devops-node-api";
 import { PullRequestStatus, GitVersionType, GitPullRequestQueryType, CommentThreadStatus } from "azure-devops-node-api/interfaces/GitInterfaces.js";
 import { configureRepoTools, REPO_TOOLS } from "../../../src/tools/repos";
-import { getCurrentUserDetails } from "../../../src/tools/auth";
+import { getCurrentUserDetails, getUserIdFromEmail } from "../../../src/tools/auth";
 
 // Mock the auth module
 jest.mock("../../../src/tools/auth", () => ({
   getCurrentUserDetails: jest.fn(),
+  getUserIdFromEmail: jest.fn(),
 }));
 
 const mockGetCurrentUserDetails = getCurrentUserDetails as jest.MockedFunction<typeof getCurrentUserDetails>;
+const mockGetUserIdFromEmail = getUserIdFromEmail as jest.MockedFunction<typeof getUserIdFromEmail>;
 
 describe("repos tools", () => {
   let server: McpServer;
@@ -543,6 +545,85 @@ describe("repos tools", () => {
       expect(mockGetCurrentUserDetails).toHaveBeenCalled();
       expect(mockGitApi.getPullRequests).toHaveBeenCalledWith("repo123", { status: PullRequestStatus.Active, repositoryId: "repo123", creatorId: "user123" }, undefined, undefined, 0, 100);
     });
+
+    it("should filter pull requests where I am a reviewer", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_repo);
+      if (!call) throw new Error("repo_list_pull_requests_by_repo tool not registered");
+      const [, , , handler] = call;
+
+      mockGitApi.getPullRequests.mockResolvedValue([]);
+
+      const params = {
+        repositoryId: "repo123",
+        i_am_reviewer: true,
+        status: "Active",
+        top: 100,
+        skip: 0,
+      };
+
+      await handler(params);
+
+      expect(mockGetCurrentUserDetails).toHaveBeenCalled();
+      expect(mockGitApi.getPullRequests).toHaveBeenCalledWith("repo123", { status: PullRequestStatus.Active, repositoryId: "repo123", reviewerId: "user123" }, undefined, undefined, 0, 100);
+    });
+
+    it("should filter pull requests created by me and where I am a reviewer", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_repo);
+      if (!call) throw new Error("repo_list_pull_requests_by_repo tool not registered");
+      const [, , , handler] = call;
+
+      mockGitApi.getPullRequests.mockResolvedValue([]);
+
+      const params = {
+        repositoryId: "repo123",
+        created_by_me: true,
+        i_am_reviewer: true,
+        status: "Active",
+        top: 100,
+        skip: 0,
+      };
+
+      await handler(params);
+
+      expect(mockGetCurrentUserDetails).toHaveBeenCalled();
+      expect(mockGitApi.getPullRequests).toHaveBeenCalledWith(
+        "repo123",
+        { status: PullRequestStatus.Active, repositoryId: "repo123", creatorId: "user123", reviewerId: "user123" },
+        undefined,
+        undefined,
+        0,
+        100
+      );
+    });
+
+    it("should filter pull requests created by specific user successfully", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_repo);
+      if (!call) throw new Error("repo_list_pull_requests_by_repo tool not registered");
+      const [, , , handler] = call;
+
+      // Mock successful user lookup
+      mockGetUserIdFromEmail.mockResolvedValue("specific-user-123");
+      mockGitApi.getPullRequests.mockResolvedValue([]);
+
+      const params = {
+        repositoryId: "repo123",
+        created_by_user: "john@example.com",
+        status: "Active",
+        top: 100,
+        skip: 0,
+      };
+
+      await handler(params);
+
+      expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("john@example.com", tokenProvider, connectionProvider, userAgentProvider);
+      expect(mockGitApi.getPullRequests).toHaveBeenCalledWith("repo123", { status: PullRequestStatus.Active, repositoryId: "repo123", creatorId: "specific-user-123" }, undefined, undefined, 0, 100);
+    });
   });
 
   describe("repo_list_pull_requests_by_project", () => {
@@ -597,6 +678,228 @@ describe("repos tools", () => {
 
       expect(result.content[0].text).toBe(JSON.stringify(expectedResult, null, 2));
     });
+
+    it("should filter by current user when created_by_me is true", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_project);
+      if (!call) throw new Error("repo_list_pull_requests_by_project tool not registered");
+      const [, , , handler] = call;
+
+      const mockPRs = [
+        {
+          pullRequestId: 123,
+          codeReviewId: 456,
+          repository: { name: "test-repo" },
+          status: PullRequestStatus.Active,
+          createdBy: { displayName: "Test User", uniqueName: "testuser@example.com" },
+          creationDate: "2023-01-01T00:00:00Z",
+          title: "My Feature PR",
+          isDraft: false,
+          sourceRefName: "refs/heads/my-feature-branch",
+          targetRefName: "refs/heads/main",
+        },
+      ];
+      mockGitApi.getPullRequestsByProject.mockResolvedValue(mockPRs);
+
+      const params = {
+        project: "test-project",
+        created_by_me: true,
+        status: "Active",
+        top: 100,
+        skip: 0,
+      };
+
+      const result = await handler(params);
+
+      expect(mockGetCurrentUserDetails).toHaveBeenCalledWith(tokenProvider, connectionProvider, userAgentProvider);
+      expect(mockGitApi.getPullRequestsByProject).toHaveBeenCalledWith("test-project", { status: PullRequestStatus.Active, creatorId: "user123" }, undefined, 0, 100);
+
+      const expectedResult = [
+        {
+          pullRequestId: 123,
+          codeReviewId: 456,
+          repository: "test-repo",
+          status: PullRequestStatus.Active,
+          createdBy: { displayName: "Test User", uniqueName: "testuser@example.com" },
+          creationDate: "2023-01-01T00:00:00Z",
+          title: "My Feature PR",
+          isDraft: false,
+          sourceRefName: "refs/heads/my-feature-branch",
+          targetRefName: "refs/heads/main",
+        },
+      ];
+
+      expect(result.content[0].text).toBe(JSON.stringify(expectedResult, null, 2));
+    });
+
+    it("should filter by current user as reviewer when i_am_reviewer is true", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_project);
+      if (!call) throw new Error("repo_list_pull_requests_by_project tool not registered");
+      const [, , , handler] = call;
+
+      const mockPRs = [
+        {
+          pullRequestId: 456,
+          codeReviewId: 789,
+          repository: { name: "test-repo" },
+          status: PullRequestStatus.Active,
+          createdBy: { displayName: "Other User", uniqueName: "other@example.com" },
+          creationDate: "2023-01-02T00:00:00Z",
+          title: "Review Me PR",
+          isDraft: false,
+          sourceRefName: "refs/heads/review-branch",
+          targetRefName: "refs/heads/main",
+        },
+      ];
+      mockGitApi.getPullRequestsByProject.mockResolvedValue(mockPRs);
+
+      const params = {
+        project: "test-project",
+        i_am_reviewer: true,
+        status: "Active",
+        top: 100,
+        skip: 0,
+      };
+
+      const result = await handler(params);
+
+      expect(mockGetCurrentUserDetails).toHaveBeenCalledWith(tokenProvider, connectionProvider, userAgentProvider);
+      expect(mockGitApi.getPullRequestsByProject).toHaveBeenCalledWith("test-project", { status: PullRequestStatus.Active, reviewerId: "user123" }, undefined, 0, 100);
+
+      const expectedResult = [
+        {
+          pullRequestId: 456,
+          codeReviewId: 789,
+          repository: "test-repo",
+          status: PullRequestStatus.Active,
+          createdBy: { displayName: "Other User", uniqueName: "other@example.com" },
+          creationDate: "2023-01-02T00:00:00Z",
+          title: "Review Me PR",
+          isDraft: false,
+          sourceRefName: "refs/heads/review-branch",
+          targetRefName: "refs/heads/main",
+        },
+      ];
+
+      expect(result.content[0].text).toBe(JSON.stringify(expectedResult, null, 2));
+    });
+
+    it("should filter by both creator and reviewer when both created_by_me and i_am_reviewer are true", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_project);
+      if (!call) throw new Error("repo_list_pull_requests_by_project tool not registered");
+      const [, , , handler] = call;
+
+      const mockPRs = [
+        {
+          pullRequestId: 789,
+          codeReviewId: 101112,
+          repository: { name: "test-repo" },
+          status: PullRequestStatus.Active,
+          createdBy: { displayName: "Test User", uniqueName: "testuser@example.com" },
+          creationDate: "2023-01-03T00:00:00Z",
+          title: "Both Creator and Reviewer PR",
+          isDraft: false,
+          sourceRefName: "refs/heads/both-branch",
+          targetRefName: "refs/heads/main",
+        },
+      ];
+      mockGitApi.getPullRequestsByProject.mockResolvedValue(mockPRs);
+
+      const params = {
+        project: "test-project",
+        created_by_me: true,
+        i_am_reviewer: true,
+        status: "Active",
+        top: 100,
+        skip: 0,
+      };
+
+      const result = await handler(params);
+
+      expect(mockGetCurrentUserDetails).toHaveBeenCalledWith(tokenProvider, connectionProvider, userAgentProvider);
+      expect(mockGitApi.getPullRequestsByProject).toHaveBeenCalledWith("test-project", { status: PullRequestStatus.Active, creatorId: "user123", reviewerId: "user123" }, undefined, 0, 100);
+
+      const expectedResult = [
+        {
+          pullRequestId: 789,
+          codeReviewId: 101112,
+          repository: "test-repo",
+          status: PullRequestStatus.Active,
+          createdBy: { displayName: "Test User", uniqueName: "testuser@example.com" },
+          creationDate: "2023-01-03T00:00:00Z",
+          title: "Both Creator and Reviewer PR",
+          isDraft: false,
+          sourceRefName: "refs/heads/both-branch",
+          targetRefName: "refs/heads/main",
+        },
+      ];
+
+      expect(result.content[0].text).toBe(JSON.stringify(expectedResult, null, 2));
+    });
+
+    it("should prioritize created_by_user over created_by_me flag", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_project);
+      if (!call) throw new Error("repo_list_pull_requests_by_project tool not registered");
+      const [, , , handler] = call;
+
+      // Mock getUserIdFromEmail to return a specific user ID
+      mockGetUserIdFromEmail.mockResolvedValue("specific-user-123");
+
+      const mockPRs = [
+        {
+          pullRequestId: 999,
+          codeReviewId: 888,
+          repository: { name: "test-repo" },
+          status: PullRequestStatus.Active,
+          createdBy: { displayName: "Specific User", uniqueName: "specific@example.com" },
+          creationDate: "2023-01-04T00:00:00Z",
+          title: "Specific User PR",
+          isDraft: false,
+          sourceRefName: "refs/heads/specific-branch",
+          targetRefName: "refs/heads/main",
+        },
+      ];
+      mockGitApi.getPullRequestsByProject.mockResolvedValue(mockPRs);
+
+      const params = {
+        project: "test-project",
+        created_by_user: "specific@example.com",
+        created_by_me: true, // This should be ignored since created_by_user takes precedence
+        status: "Active",
+        top: 100,
+        skip: 0,
+      };
+
+      const result = await handler(params);
+
+      expect(mockGetUserIdFromEmail).toHaveBeenCalledWith("specific@example.com", tokenProvider, connectionProvider, userAgentProvider);
+      expect(mockGetCurrentUserDetails).not.toHaveBeenCalled(); // Should not be called when created_by_user is provided
+      expect(mockGitApi.getPullRequestsByProject).toHaveBeenCalledWith("test-project", { status: PullRequestStatus.Active, creatorId: "specific-user-123" }, undefined, 0, 100);
+
+      const expectedResult = [
+        {
+          pullRequestId: 999,
+          codeReviewId: 888,
+          repository: "test-repo",
+          status: PullRequestStatus.Active,
+          createdBy: { displayName: "Specific User", uniqueName: "specific@example.com" },
+          creationDate: "2023-01-04T00:00:00Z",
+          title: "Specific User PR",
+          isDraft: false,
+          sourceRefName: "refs/heads/specific-branch",
+          targetRefName: "refs/heads/main",
+        },
+      ];
+
+      expect(result.content[0].text).toBe(JSON.stringify(expectedResult, null, 2));
+    });
   });
 
   describe("repo_list_pull_request_threads", () => {
@@ -738,27 +1041,78 @@ describe("repos tools", () => {
 
       expect(result.content[0].text).toBe(JSON.stringify(expectedResult, null, 2));
     });
-  });
 
-  describe("repo_list_branches_by_repo", () => {
-    it("should list branches by repository", async () => {
+    it("should list pull request thread comments with full response", async () => {
       configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
 
-      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_branches_by_repo);
-      if (!call) throw new Error("repo_list_branches_by_repo tool not registered");
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_request_thread_comments);
+      if (!call) throw new Error("repo_list_pull_request_thread_comments tool not registered");
       const [, , , handler] = call;
 
-      const mockBranches = [
-        { name: "refs/heads/main" },
-        { name: "refs/heads/feature-1" },
-        { name: "refs/heads/feature-2" },
-        { name: "refs/tags/v1.0" }, // Should be filtered out
+      const mockComments = [
+        {
+          id: 1,
+          author: { displayName: "John Doe", uniqueName: "john@example.com" },
+          content: "This looks good",
+          publishedDate: "2023-01-01T00:00:00Z",
+          lastUpdatedDate: "2023-01-01T00:30:00Z",
+          lastContentUpdatedDate: "2023-01-01T00:15:00Z",
+          isDeleted: false,
+          // Additional properties that would be in full response
+          commentType: 1,
+          usersLiked: [],
+          parentCommentId: 0,
+        },
+        {
+          id: 2,
+          author: { displayName: "Jane Doe", uniqueName: "jane@example.com" },
+          content: "Deleted comment",
+          publishedDate: "2023-01-01T01:00:00Z",
+          isDeleted: true,
+          commentType: 1,
+          usersLiked: [],
+          parentCommentId: 0,
+        },
       ];
-      mockGitApi.getRefs.mockResolvedValue(mockBranches);
+      mockGitApi.getComments.mockResolvedValue(mockComments);
 
       const params = {
         repositoryId: "repo123",
-        top: 100,
+        pullRequestId: 456,
+        threadId: 789,
+        top: 100,
+        skip: 0,
+        fullResponse: true,
+      };
+
+      const result = await handler(params);
+
+      expect(mockGitApi.getComments).toHaveBeenCalledWith("repo123", 456, 789, undefined);
+
+      // When fullResponse is true, it should return the full comment objects without trimming
+      expect(result.content[0].text).toBe(JSON.stringify(mockComments, null, 2));
+    });
+  });
+
+  describe("repo_list_branches_by_repo", () => {
+    it("should list branches by repository", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_branches_by_repo);
+      if (!call) throw new Error("repo_list_branches_by_repo tool not registered");
+      const [, , , handler] = call;
+
+      const mockBranches = [
+        { name: "refs/heads/main" },
+        { name: "refs/heads/feature-1" },
+        { name: "refs/heads/feature-2" },
+        { name: "refs/tags/v1.0" }, // Should be filtered out
+      ];
+      mockGitApi.getRefs.mockResolvedValue(mockBranches);
+
+      const params = {
+        repositoryId: "repo123",
+        top: 100,
       };
 
       const result = await handler(params);
@@ -1313,4 +1667,1290 @@ describe("repos tools", () => {
       expect(result.content[0].text).toContain("Error querying pull requests by commits: Query Error");
     });
   });
+
+  describe("pullRequestStatusStringToInt function coverage", () => {
+    it("should handle Completed status", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_repo);
+      if (!call) throw new Error("repo_list_pull_requests_by_repo tool not registered");
+      const [, , , handler] = call;
+
+      mockGetCurrentUserDetails.mockResolvedValue({
+        authenticatedUser: { id: "user123" },
+      });
+
+      mockGitApi.getPullRequests.mockResolvedValue([]);
+
+      const params = {
+        repositoryId: "repo123",
+        status: "Completed",
+        top: 100,
+        skip: 0,
+      };
+
+      await handler(params);
+
+      expect(mockGitApi.getPullRequests).toHaveBeenCalledWith("repo123", { status: PullRequestStatus.Completed, repositoryId: "repo123" }, undefined, undefined, 0, 100);
+    });
+
+    it("should handle All status", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_repo);
+      if (!call) throw new Error("repo_list_pull_requests_by_repo tool not registered");
+      const [, , , handler] = call;
+
+      mockGitApi.getPullRequests.mockResolvedValue([]);
+
+      const params = {
+        repositoryId: "repo123",
+        status: "All",
+        top: 100,
+        skip: 0,
+      };
+
+      await handler(params);
+
+      expect(mockGitApi.getPullRequests).toHaveBeenCalledWith("repo123", { status: PullRequestStatus.All, repositoryId: "repo123" }, undefined, undefined, 0, 100);
+    });
+
+    it("should handle NotSet status", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_repo);
+      if (!call) throw new Error("repo_list_pull_requests_by_repo tool not registered");
+      const [, , , handler] = call;
+
+      mockGitApi.getPullRequests.mockResolvedValue([]);
+
+      const params = {
+        repositoryId: "repo123",
+        status: "NotSet",
+        top: 100,
+        skip: 0,
+      };
+
+      await handler(params);
+
+      expect(mockGitApi.getPullRequests).toHaveBeenCalledWith("repo123", { status: PullRequestStatus.NotSet, repositoryId: "repo123" }, undefined, undefined, 0, 100);
+    });
+
+    it("should handle Abandoned status", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_repo);
+      if (!call) throw new Error("repo_list_pull_requests_by_repo tool not registered");
+      const [, , , handler] = call;
+
+      mockGitApi.getPullRequests.mockResolvedValue([]);
+
+      const params = {
+        repositoryId: "repo123",
+        status: "Abandoned",
+        top: 100,
+        skip: 0,
+      };
+
+      await handler(params);
+
+      expect(mockGitApi.getPullRequests).toHaveBeenCalledWith("repo123", { status: PullRequestStatus.Abandoned, repositoryId: "repo123" }, undefined, undefined, 0, 100);
+    });
+
+    it("should throw error for unknown status", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_repo);
+      if (!call) throw new Error("repo_list_pull_requests_by_repo tool not registered");
+      const [, , , handler] = call;
+
+      const params = {
+        repositoryId: "repo123",
+        status: "InvalidStatus",
+        top: 100,
+        skip: 0,
+      };
+
+      await expect(handler(params)).rejects.toThrow("Unknown pull request status: InvalidStatus");
+    });
+  });
+
+  describe("error handling coverage", () => {
+    it("should handle getUserIdFromEmail error in list_pull_requests_by_repo", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_repo);
+      if (!call) throw new Error("repo_list_pull_requests_by_repo tool not registered");
+      const [, , , handler] = call;
+
+      // Mock getUserIdFromEmail to throw an error
+      mockGetUserIdFromEmail.mockRejectedValue(new Error("User not found"));
+
+      const params = {
+        repositoryId: "repo123",
+        created_by_user: "nonexistent@example.com",
+        status: "Active",
+        top: 100,
+        skip: 0,
+      };
+
+      const result = await handler(params);
+
+      expect(result.isError).toBe(true);
+      expect(result.content[0].text).toContain("Error finding user with email nonexistent@example.com: User not found");
+    });
+
+    it("should handle getUserIdFromEmail error in list_pull_requests_by_project", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_project);
+      if (!call) throw new Error("repo_list_pull_requests_by_project tool not registered");
+      const [, , , handler] = call;
+
+      // Mock getUserIdFromEmail to throw an error
+      mockGetUserIdFromEmail.mockRejectedValue(new Error("User not found"));
+
+      const params = {
+        project: "test-project",
+        created_by_user: "nonexistent@example.com",
+        status: "Active",
+        top: 100,
+        skip: 0,
+      };
+
+      const result = await handler(params);
+
+      expect(result.isError).toBe(true);
+      expect(result.content[0].text).toContain("Error finding user with email nonexistent@example.com: User not found");
+    });
+
+    it("should handle commit search error in search_commits", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.search_commits);
+      if (!call) throw new Error("repo_search_commits tool not registered");
+      const [, , , handler] = call;
+
+      mockGitApi.getCommits.mockRejectedValue(new Error("API Error"));
+
+      const params = {
+        project: "test-project",
+        repository: "test-repo",
+      };
+
+      const result = await handler(params);
+
+      expect(result.isError).toBe(true);
+      expect(result.content[0].text).toContain("Error searching commits: API Error");
+    });
+
+    it("should handle thread creation error", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.create_pull_request_thread);
+      if (!call) throw new Error("repo_create_pull_request_thread tool not registered");
+      const [, , , handler] = call;
+
+      mockGitApi.createThread.mockRejectedValue(new Error("Thread creation failed"));
+
+      const params = {
+        repositoryId: "repo123",
+        pullRequestId: 456,
+        content: "Test comment",
+      };
+
+      await expect(handler(params)).rejects.toThrow("Thread creation failed");
+    });
+
+    it("should handle thread resolution error", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.resolve_comment);
+      if (!call) throw new Error("repo_resolve_comment tool not registered");
+      const [, , , handler] = call;
+
+      mockGitApi.updateThread.mockRejectedValue(new Error("Thread resolution failed"));
+
+      const params = {
+        repositoryId: "repo123",
+        pullRequestId: 456,
+        threadId: 789,
+      };
+
+      await expect(handler(params)).rejects.toThrow("Thread resolution failed");
+    });
+
+    it("should handle comment reply error", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.reply_to_comment);
+      if (!call) throw new Error("repo_reply_to_comment tool not registered");
+      const [, , , handler] = call;
+
+      mockGitApi.createComment.mockRejectedValue(new Error("Comment creation failed"));
+
+      const params = {
+        repositoryId: "repo123",
+        pullRequestId: 456,
+        threadId: 789,
+        content: "Test reply",
+      };
+
+      await expect(handler(params)).rejects.toThrow("Comment creation failed");
+    });
+  });
+
+  describe("edge cases and validation", () => {
+    it("should handle invalid line numbers in create_pull_request_thread", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.create_pull_request_thread);
+      if (!call) throw new Error("repo_create_pull_request_thread tool not registered");
+      const [, , , handler] = call;
+
+      const params = {
+        repositoryId: "repo123",
+        pullRequestId: 456,
+        content: "Test comment",
+        filePath: "/test/file.js",
+        rightFileStartLine: 0, // Invalid line number (should be >= 1)
+      };
+
+      await expect(handler(params)).rejects.toThrow("rightFileStartLine must be greater than or equal to 1.");
+    });
+
+    it("should handle create_pull_request with undefined forkSourceRepositoryId", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.create_pull_request);
+      if (!call) throw new Error("repo_create_pull_request tool not registered");
+      const [, , , handler] = call;
+
+      const mockPR = { pullRequestId: 123, title: "Test PR" };
+      mockGitApi.createPullRequest.mockResolvedValue(mockPR);
+
+      const params = {
+        repositoryId: "repo123",
+        sourceRefName: "refs/heads/feature",
+        targetRefName: "refs/heads/main",
+        title: "Test PR",
+        description: undefined,
+        isDraft: undefined,
+        // forkSourceRepositoryId is undefined - should test the branch where it's undefined
+      };
+
+      const result = await handler(params);
+
+      expect(mockGitApi.createPullRequest).toHaveBeenCalledWith(
+        {
+          sourceRefName: "refs/heads/feature",
+          targetRefName: "refs/heads/main",
+          title: "Test PR",
+          description: undefined,
+          isDraft: undefined, // This is what actually gets passed when isDraft is not provided
+          workItemRefs: [],
+          forkSource: undefined, // This should be undefined when forkSourceRepositoryId is not provided
+        },
+        "repo123"
+      );
+
+      expect(result.content[0].text).toBe(JSON.stringify(mockPR, null, 2));
+    });
+
+    it("should handle trimComments with undefined comments", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_request_threads);
+      if (!call) throw new Error("repo_list_pull_request_threads tool not registered");
+      const [, , , handler] = call;
+
+      // Mock threads with undefined comments to test the trimComments function
+      const mockThreads = [
+        {
+          id: 1,
+          publishedDate: "2023-01-01T00:00:00Z",
+          lastUpdatedDate: "2023-01-01T00:00:00Z",
+          status: 1,
+          comments: undefined, // undefined comments
+        },
+        {
+          id: 2,
+          publishedDate: "2023-01-02T00:00:00Z",
+          lastUpdatedDate: "2023-01-02T00:00:00Z",
+          status: 1,
+          comments: null, // null comments
+        },
+      ];
+
+      mockGitApi.getThreads.mockResolvedValue(mockThreads);
+
+      const params = {
+        repositoryId: "repo123",
+        pullRequestId: 456,
+        top: 10,
+        skip: 0,
+      };
+
+      const result = await handler(params);
+
+      const resultData = JSON.parse(result.content[0].text);
+      expect(resultData).toHaveLength(2);
+      expect(resultData[0].comments).toBeUndefined();
+      expect(resultData[1].comments).toBeUndefined();
+    });
+
+    it("should handle trimComments with deleted comments", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_request_threads);
+      if (!call) throw new Error("repo_list_pull_request_threads tool not registered");
+      const [, , , handler] = call;
+
+      // Mock threads with deleted comments to test the trimComments function
+      const mockThreads = [
+        {
+          id: 1,
+          publishedDate: "2023-01-01T00:00:00Z",
+          lastUpdatedDate: "2023-01-01T00:00:00Z",
+          status: 1,
+          comments: [
+            {
+              id: 1,
+              content: "This is a normal comment",
+              isDeleted: false,
+              author: { displayName: "User 1", uniqueName: "user1@example.com" },
+            },
+            {
+              id: 2,
+              content: "This comment was deleted",
+              isDeleted: true, // This should be filtered out
+              author: { displayName: "User 2", uniqueName: "user2@example.com" },
+            },
+          ],
+        },
+      ];
+
+      mockGitApi.getThreads.mockResolvedValue(mockThreads);
+
+      const params = {
+        repositoryId: "repo123",
+        pullRequestId: 456,
+        top: 10,
+        skip: 0,
+      };
+
+      const result = await handler(params);
+
+      const resultData = JSON.parse(result.content[0].text);
+      expect(resultData).toHaveLength(1);
+      expect(resultData[0].comments).toHaveLength(1); // Only non-deleted comment should remain
+      expect(resultData[0].comments[0].id).toBe(1);
+    });
+
+    it("should handle list_repos_by_project without repoNameFilter", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_repos_by_project);
+      if (!call) throw new Error("repo_list_repos_by_project tool not registered");
+      const [, , , handler] = call;
+
+      const mockRepos = [
+        { id: "1", name: "repo1", isDisabled: false, isFork: false, isInMaintenance: false, webUrl: "http://example.com/repo1", size: 100 },
+        { id: "2", name: "repo2", isDisabled: false, isFork: false, isInMaintenance: false, webUrl: "http://example.com/repo2", size: 200 },
+      ];
+
+      mockGitApi.getRepositories.mockResolvedValue(mockRepos);
+
+      const params = {
+        project: "test-project",
+        top: 100,
+        skip: 0,
+        // repoNameFilter is undefined - should test the branch where it's not provided
+      };
+
+      const result = await handler(params);
+
+      const resultData = JSON.parse(result.content[0].text);
+      expect(resultData).toHaveLength(2); // All repos should be returned when no filter is applied
+      expect(resultData[0].name).toBe("repo1");
+      expect(resultData[1].name).toBe("repo2");
+    });
+
+    it("should handle branches.find returning undefined (branch name mismatch)", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.get_branch_by_name);
+      if (!call) throw new Error("repo_get_branch_by_name tool not registered");
+      const [, , , handler] = call;
+
+      // Mock branches that don't match the requested branch name
+      const mockBranches = [
+        { name: "refs/heads/other-branch", objectId: "abc123" },
+        { name: "refs/heads/another-branch", objectId: "def456" },
+      ];
+
+      mockGitApi.getRefs.mockResolvedValue(mockBranches);
+
+      const params = {
+        repositoryId: "repo123",
+        branchName: "nonexistent-branch", // This branch doesn't exist in the mock data
+      };
+
+      const result = await handler(params);
+
+      expect(result.isError).toBe(true);
+      expect(result.content[0].text).toBe("Branch nonexistent-branch not found in repository repo123");
+    });
+
+    it("should handle branch.name with exact branchName match", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.get_branch_by_name);
+      if (!call) throw new Error("repo_get_branch_by_name tool not registered");
+      const [, , , handler] = call;
+
+      // Mock branches where one matches exactly with the branchName (second condition in the find)
+      const mockBranches = [
+        { name: "refs/heads/other-branch", objectId: "abc123" },
+        { name: "main", objectId: "def456" }, // This matches the branchName directly
+      ];
+
+      mockGitApi.getRefs.mockResolvedValue(mockBranches);
+
+      const params = {
+        repositoryId: "repo123",
+        branchName: "main",
+      };
+
+      const result = await handler(params);
+
+      expect(result.isError).toBeUndefined();
+      expect(JSON.parse(result.content[0].text).name).toBe("main");
+    });
+
+    it("should handle list_pull_requests_by_repo with created_by_user and i_am_reviewer both false", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_repo);
+      if (!call) throw new Error("repo_list_pull_requests_by_repo tool not registered");
+      const [, , , handler] = call;
+
+      mockGitApi.getPullRequests.mockResolvedValue([]);
+
+      const params = {
+        repositoryId: "repo123",
+        status: "Active", // Provide explicit status to avoid undefined
+        created_by_me: false,
+        i_am_reviewer: false,
+        top: 100, // Explicit defaults
+        skip: 0, // Explicit defaults
+        // created_by_user is undefined - should test the case where we don't call getCurrentUserDetails
+      };
+
+      await handler(params);
+
+      // getCurrentUserDetails should not be called when both flags are false and created_by_user is undefined
+      expect(mockGetCurrentUserDetails).not.toHaveBeenCalled();
+      expect(mockGitApi.getPullRequests).toHaveBeenCalledWith(
+        "repo123",
+        { status: PullRequestStatus.Active, repositoryId: "repo123" },
+        undefined,
+        undefined,
+        0, // skip
+        100 // top
+      );
+    });
+
+    it("should handle list_pull_requests_by_project with created_by_user and i_am_reviewer both false", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_project);
+      if (!call) throw new Error("repo_list_pull_requests_by_project tool not registered");
+      const [, , , handler] = call;
+
+      mockGitApi.getPullRequestsByProject.mockResolvedValue([]);
+
+      const params = {
+        project: "test-project",
+        status: "Active", // Provide explicit status to avoid undefined
+        created_by_me: false,
+        i_am_reviewer: false,
+        top: 100, // Explicit defaults
+        skip: 0, // Explicit defaults
+        // created_by_user is undefined - should test the case where we don't call getCurrentUserDetails
+      };
+
+      await handler(params);
+
+      // getCurrentUserDetails should not be called when both flags are false and created_by_user is undefined
+      expect(mockGetCurrentUserDetails).not.toHaveBeenCalled();
+      expect(mockGitApi.getPullRequestsByProject).toHaveBeenCalledWith(
+        "test-project",
+        { status: PullRequestStatus.Active },
+        undefined,
+        0, // skip
+        100 // top
+      );
+    });
+
+    it("should handle comments?.flatMap with null/undefined branch in branchesFilterOutIrrelevantProperties", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_branches_by_repo);
+      if (!call) throw new Error("repo_list_branches_by_repo tool not registered");
+      const [, , , handler] = call;
+
+      // Mock branches with some having null/undefined names to test the flatMap filter
+      const mockBranches = [
+        { name: "refs/heads/main", objectId: "abc123" },
+        { name: null, objectId: "def456" }, // null name should be filtered out
+        { name: undefined, objectId: "ghi789" }, // undefined name should be filtered out
+        { name: "refs/heads/feature", objectId: "jkl012" },
+        { name: "refs/tags/v1.0", objectId: "mno345" }, // not a heads/ ref, should be filtered out
+      ];
+
+      mockGitApi.getRefs.mockResolvedValue(mockBranches);
+
+      const params = {
+        repositoryId: "repo123",
+      };
+
+      const result = await handler(params);
+
+      const resultData = JSON.parse(result.content[0].text);
+      // Should only include valid heads/ refs with names
+      expect(resultData).toEqual(["main", "feature"]);
+    });
+
+    it("should handle rightFileStartOffset without validation error", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.create_pull_request_thread);
+      if (!call) throw new Error("repo_create_pull_request_thread tool not registered");
+      const [, , , handler] = call;
+
+      const mockThread = { id: 123, status: 1, comments: [] };
+      mockGitApi.createThread.mockResolvedValue(mockThread);
+
+      const params = {
+        repositoryId: "repo123",
+        pullRequestId: 456,
+        content: "Test comment",
+        filePath: "/test/file.js",
+        status: "Active", // Provide explicit status
+        rightFileStartLine: 5,
+        rightFileStartOffset: 10, // Valid offset
+      };
+
+      const result = await handler(params);
+
+      expect(mockGitApi.createThread).toHaveBeenCalledWith(
+        {
+          comments: [{ content: "Test comment" }],
+          threadContext: {
+            filePath: "/test/file.js",
+            rightFileStart: { line: 5, offset: 10 },
+          },
+          status: CommentThreadStatus.Active,
+        },
+        "repo123",
+        456,
+        undefined
+      );
+
+      expect(result.content[0].text).toBe(JSON.stringify(mockThread, null, 2));
+    });
+
+    it("should handle rightFileEndOffset without validation error", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.create_pull_request_thread);
+      if (!call) throw new Error("repo_create_pull_request_thread tool not registered");
+      const [, , , handler] = call;
+
+      const mockThread = { id: 123, status: 1, comments: [] };
+      mockGitApi.createThread.mockResolvedValue(mockThread);
+
+      const params = {
+        repositoryId: "repo123",
+        pullRequestId: 456,
+        content: "Test comment",
+        filePath: "/test/file.js",
+        status: "Active", // Provide explicit status
+        rightFileStartLine: 5,
+        rightFileEndLine: 10,
+        rightFileEndOffset: 15, // Valid end offset
+      };
+
+      const result = await handler(params);
+
+      expect(mockGitApi.createThread).toHaveBeenCalledWith(
+        {
+          comments: [{ content: "Test comment" }],
+          threadContext: {
+            filePath: "/test/file.js",
+            rightFileStart: { line: 5 },
+            rightFileEnd: { line: 10, offset: 15 },
+          },
+          status: CommentThreadStatus.Active,
+        },
+        "repo123",
+        456,
+        undefined
+      );
+
+      expect(result.content[0].text).toBe(JSON.stringify(mockThread, null, 2));
+    });
+
+    it("should handle search_commits with version parameter", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.search_commits);
+      if (!call) throw new Error("repo_search_commits tool not registered");
+      const [, , , handler] = call;
+
+      const mockCommits = [{ commitId: "abc123", comment: "Test commit" }];
+      mockGitApi.getCommits.mockResolvedValue(mockCommits);
+
+      const params = {
+        project: "test-project",
+        repository: "test-repo",
+        version: "main", // This should trigger the version branch
+        versionType: "Branch",
+        skip: 0, // Provide explicit values
+        top: 10,
+        includeLinks: false,
+        includeWorkItems: false,
+      };
+
+      const result = await handler(params);
+
+      expect(mockGitApi.getCommits).toHaveBeenCalledWith(
+        "test-repo",
+        {
+          fromCommitId: undefined,
+          toCommitId: undefined,
+          includeLinks: false,
+          includeWorkItems: false,
+          itemVersion: {
+            version: "main",
+            versionType: GitVersionType.Branch,
+          },
+        },
+        "test-project",
+        0,
+        10
+      );
+
+      expect(result.content[0].text).toBe(JSON.stringify(mockCommits, null, 2));
+    });
+
+    it("should handle search_commits without version parameter", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.search_commits);
+      if (!call) throw new Error("repo_search_commits tool not registered");
+      const [, , , handler] = call;
+
+      const mockCommits = [{ commitId: "abc123", comment: "Test commit" }];
+      mockGitApi.getCommits.mockResolvedValue(mockCommits);
+
+      const params = {
+        project: "test-project",
+        repository: "test-repo",
+        skip: 0, // Provide explicit values
+        top: 10,
+        includeLinks: false,
+        includeWorkItems: false,
+        // version is undefined - should test the branch where itemVersion is not set
+      };
+
+      const result = await handler(params);
+
+      expect(mockGitApi.getCommits).toHaveBeenCalledWith(
+        "test-repo",
+        {
+          fromCommitId: undefined,
+          toCommitId: undefined,
+          includeLinks: false,
+          includeWorkItems: false,
+          // itemVersion should not be set when version is undefined
+        },
+        "test-project",
+        0,
+        10
+      );
+
+      expect(result.content[0].text).toBe(JSON.stringify(mockCommits, null, 2));
+    });
+
+    it("should handle rightFileEndLine without rightFileStartLine", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.create_pull_request_thread);
+      if (!call) throw new Error("repo_create_pull_request_thread tool not registered");
+      const [, , , handler] = call;
+
+      const params = {
+        repositoryId: "repo123",
+        pullRequestId: 456,
+        content: "Test comment",
+        filePath: "/test/file.js",
+        rightFileEndLine: 10, // End line specified without start line
+      };
+
+      await expect(handler(params)).rejects.toThrow("rightFileEndLine must only be specified if rightFileStartLine is also specified.");
+    });
+
+    it("should handle invalid rightFileEndLine value", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.create_pull_request_thread);
+      if (!call) throw new Error("repo_create_pull_request_thread tool not registered");
+      const [, , , handler] = call;
+
+      const params = {
+        repositoryId: "repo123",
+        pullRequestId: 456,
+        content: "Test comment",
+        filePath: "/test/file.js",
+        rightFileStartLine: 5,
+        rightFileEndLine: 0, // Invalid end line
+      };
+
+      await expect(handler(params)).rejects.toThrow("rightFileEndLine must be greater than or equal to 1.");
+    });
+
+    it("should handle invalid rightFileStartOffset value", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.create_pull_request_thread);
+      if (!call) throw new Error("repo_create_pull_request_thread tool not registered");
+      const [, , , handler] = call;
+
+      const params = {
+        repositoryId: "repo123",
+        pullRequestId: 456,
+        content: "Test comment",
+        filePath: "/test/file.js",
+        rightFileStartLine: 5,
+        rightFileStartOffset: 0, // Invalid offset
+      };
+
+      await expect(handler(params)).rejects.toThrow("rightFileStartOffset must be greater than or equal to 1.");
+    });
+
+    it("should handle invalid rightFileEndOffset value", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.create_pull_request_thread);
+      if (!call) throw new Error("repo_create_pull_request_thread tool not registered");
+      const [, , , handler] = call;
+
+      const params = {
+        repositoryId: "repo123",
+        pullRequestId: 456,
+        content: "Test comment",
+        filePath: "/test/file.js",
+        rightFileStartLine: 5,
+        rightFileEndLine: 10,
+        rightFileEndOffset: 0, // Invalid offset
+      };
+
+      await expect(handler(params)).rejects.toThrow("rightFileEndOffset must be greater than or equal to 1.");
+    });
+
+    it("should test pullRequestStatusStringToInt with unknown status", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_repo);
+      if (!call) throw new Error("repo_list_pull_requests_by_repo tool not registered");
+      const [, , , handler] = call;
+
+      const params = {
+        repositoryId: "repo123",
+        status: "UnknownStatus" as "Active", // Invalid status that should trigger the default case
+        created_by_me: false,
+        i_am_reviewer: false,
+      };
+
+      await expect(handler(params)).rejects.toThrow("Unknown pull request status: UnknownStatus");
+    });
+
+    it("should handle threads?.sort with undefined id values", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_request_threads);
+      if (!call) throw new Error("repo_list_pull_request_threads tool not registered");
+      const [, , , handler] = call;
+
+      // Mock threads with undefined/null id values to test the sort function
+      const mockThreads = [
+        {
+          id: undefined, // undefined id
+          publishedDate: "2023-01-03T00:00:00Z",
+          lastUpdatedDate: "2023-01-03T00:00:00Z",
+          status: 1,
+          comments: [],
+        },
+        {
+          id: 2,
+          publishedDate: "2023-01-02T00:00:00Z",
+          lastUpdatedDate: "2023-01-02T00:00:00Z",
+          status: 1,
+          comments: [],
+        },
+        {
+          id: null, // null id
+          publishedDate: "2023-01-01T00:00:00Z",
+          lastUpdatedDate: "2023-01-01T00:00:00Z",
+          status: 1,
+          comments: [],
+        },
+      ];
+
+      mockGitApi.getThreads.mockResolvedValue(mockThreads);
+
+      const params = {
+        repositoryId: "repo123",
+        pullRequestId: 456,
+        top: 10,
+        skip: 0,
+      };
+
+      const result = await handler(params);
+
+      const resultData = JSON.parse(result.content[0].text);
+      expect(resultData).toHaveLength(3); // All threads should be returned even with undefined/null ids
+    });
+
+    it("should handle comments?.sort with undefined id values", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_request_thread_comments);
+      if (!call) throw new Error("repo_list_pull_request_thread_comments tool not registered");
+      const [, , , handler] = call;
+
+      // Mock comments with undefined/null id values to test the sort function
+      const mockComments = [
+        {
+          id: undefined, // undefined id
+          content: "Comment with undefined id",
+          isDeleted: false,
+          author: { displayName: "User 1", uniqueName: "user1@example.com" },
+        },
+        {
+          id: 2,
+          content: "Comment with id 2",
+          isDeleted: false,
+          author: { displayName: "User 2", uniqueName: "user2@example.com" },
+        },
+        {
+          id: null, // null id
+          content: "Comment with null id",
+          isDeleted: false,
+          author: { displayName: "User 3", uniqueName: "user3@example.com" },
+        },
+      ];
+
+      mockGitApi.getComments.mockResolvedValue(mockComments);
+
+      const params = {
+        repositoryId: "repo123",
+        pullRequestId: 456,
+        threadId: 789,
+        top: 10,
+        skip: 0,
+      };
+
+      const result = await handler(params);
+
+      const resultData = JSON.parse(result.content[0].text);
+      expect(resultData).toHaveLength(3); // All comments should be returned even with undefined/null ids
+    });
+
+    it("should handle workItemRefs when workItems is undefined", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.create_pull_request);
+      if (!call) throw new Error("repo_create_pull_request tool not registered");
+      const [, , , handler] = call;
+
+      const mockPR = { pullRequestId: 123, title: "Test PR" };
+      mockGitApi.createPullRequest.mockResolvedValue(mockPR);
+
+      const params = {
+        repositoryId: "repo123",
+        sourceRefName: "refs/heads/feature",
+        targetRefName: "refs/heads/main",
+        title: "Test PR",
+        // workItems is undefined - should test the ternary operator
+      };
+
+      await handler(params);
+
+      expect(mockGitApi.createPullRequest).toHaveBeenCalledWith(
+        expect.objectContaining({
+          workItemRefs: [], // Should be empty array when workItems is undefined
+        }),
+        "repo123"
+      );
+    });
+
+    it("should handle workItemRefs when workItems is provided", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.create_pull_request);
+      if (!call) throw new Error("repo_create_pull_request tool not registered");
+      const [, , , handler] = call;
+
+      const mockPR = { pullRequestId: 123, title: "Test PR" };
+      mockGitApi.createPullRequest.mockResolvedValue(mockPR);
+
+      const params = {
+        repositoryId: "repo123",
+        sourceRefName: "refs/heads/feature",
+        targetRefName: "refs/heads/main",
+        title: "Test PR",
+        workItems: "123 456", // workItems provided - should be split and mapped
+      };
+
+      await handler(params);
+
+      expect(mockGitApi.createPullRequest).toHaveBeenCalledWith(
+        expect.objectContaining({
+          workItemRefs: [{ id: "123" }, { id: "456" }], // Should be split and mapped
+        }),
+        "repo123"
+      );
+    });
+
+    it("should handle empty repoNameFilter in list_repos_by_project", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_repos_by_project);
+      if (!call) throw new Error("repo_list_repos_by_project tool not registered");
+      const [, , , handler] = call;
+
+      const mockRepos = [{ id: "repo1", name: "Repository 1", isDisabled: false, isFork: false, isInMaintenance: false, webUrl: "url1", size: 1024 }];
+      mockGitApi.getRepositories.mockResolvedValue(mockRepos);
+
+      const params = {
+        project: "test-project",
+        repoNameFilter: "", // Empty string - should use all repositories
+        top: 100,
+        skip: 0,
+      };
+
+      const result = await handler(params);
+
+      // Should return all repositories since empty string is falsy
+      const parsedResult = JSON.parse(result.content[0].text);
+      expect(parsedResult).toHaveLength(1);
+      expect(parsedResult[0].name).toBe("Repository 1");
+    });
+
+    it("should handle getUserIdFromEmail error with created_by_user in list_pull_requests_by_repo", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_repo);
+      if (!call) throw new Error("repo_list_pull_requests_by_repo tool not registered");
+      const [, , , handler] = call;
+
+      mockGetUserIdFromEmail.mockRejectedValue(new Error("User not found"));
+
+      const params = {
+        repositoryId: "repo123",
+        created_by_user: "nonexistent@example.com",
+        status: "Active",
+        top: 100,
+        skip: 0,
+      };
+
+      const result = await handler(params);
+
+      expect(result.isError).toBe(true);
+      expect(result.content[0].text).toContain("Error finding user with email nonexistent@example.com: User not found");
+    });
+
+    it("should handle getUserIdFromEmail error with created_by_user in list_pull_requests_by_project", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_project);
+      if (!call) throw new Error("repo_list_pull_requests_by_project tool not registered");
+      const [, , , handler] = call;
+
+      mockGetUserIdFromEmail.mockRejectedValue(new Error("User not found"));
+
+      const params = {
+        project: "test-project",
+        created_by_user: "nonexistent@example.com",
+        status: "Active",
+        top: 100,
+        skip: 0,
+      };
+
+      const result = await handler(params);
+
+      expect(result.isError).toBe(true);
+      expect(result.content[0].text).toContain("Error finding user with email nonexistent@example.com: User not found");
+    });
+
+    it("should handle rightFileEndOffset set without rightFileEndLine in create_pull_request_thread", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.create_pull_request_thread);
+      if (!call) throw new Error("repo_create_pull_request_thread tool not registered");
+      const [, , , handler] = call;
+
+      const mockThread = { id: 1, status: CommentThreadStatus.Active };
+      mockGitApi.createThread.mockResolvedValue(mockThread);
+
+      const params = {
+        repositoryId: "repo123",
+        pullRequestId: 456,
+        content: "Test comment",
+        filePath: "/test/file.js",
+        rightFileStartLine: 5,
+        rightFileStartOffset: 10,
+        rightFileEndOffset: 20, // End offset without end line - should still work
+      };
+
+      const result = await handler(params);
+
+      expect(mockGitApi.createThread).toHaveBeenCalled();
+      expect(result.content[0].text).toBe(JSON.stringify(mockThread, null, 2));
+    });
+
+    it("should handle error in list_pull_requests_by_commits", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_commits);
+      if (!call) throw new Error("repo_list_pull_requests_by_commits tool not registered");
+      const [, , , handler] = call;
+
+      mockGitApi.getPullRequestQuery.mockRejectedValue(new Error("API error"));
+
+      const params = {
+        project: "test-project",
+        repository: "test-repo",
+        commits: ["abc123", "def456"],
+        queryType: "LastMergeCommit",
+      };
+
+      const result = await handler(params);
+
+      expect(result.isError).toBe(true);
+      expect(result.content[0].text).toContain("Error querying pull requests by commits: API error");
+    });
+
+    it("should handle different queryType values in list_pull_requests_by_commits", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_commits);
+      if (!call) throw new Error("repo_list_pull_requests_by_commits tool not registered");
+      const [, , , handler] = call;
+
+      const mockQueryResult = { results: [] };
+      mockGitApi.getPullRequestQuery.mockResolvedValue(mockQueryResult);
+
+      const params = {
+        project: "test-project",
+        repository: "test-repo",
+        commits: ["abc123"],
+        queryType: "Commit",
+      };
+
+      const result = await handler(params);
+
+      expect(mockGitApi.getPullRequestQuery).toHaveBeenCalledWith(
+        expect.objectContaining({
+          queries: [
+            expect.objectContaining({
+              items: ["abc123"],
+              type: expect.any(Number), // Should be the enum value for Commit
+            }),
+          ],
+        }),
+        "test-repo",
+        "test-project"
+      );
+      expect(result.content[0].text).toBe(JSON.stringify(mockQueryResult, null, 2));
+    });
+
+    it("should handle repositories with null/undefined names in sorting", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_repos_by_project);
+      if (!call) throw new Error("repo_list_repos_by_project tool not registered");
+      const [, , , handler] = call;
+
+      const mockRepos = [
+        { id: "repo1", name: undefined, isDisabled: false, isFork: false, isInMaintenance: false, webUrl: "url1", size: 1024 },
+        { id: "repo2", name: "Repository B", isDisabled: false, isFork: false, isInMaintenance: false, webUrl: "url2", size: 2048 },
+        { id: "repo3", name: null, isDisabled: false, isFork: false, isInMaintenance: false, webUrl: "url3", size: 3072 },
+      ];
+      mockGitApi.getRepositories.mockResolvedValue(mockRepos);
+
+      const params = {
+        project: "test-project",
+        top: 100,
+        skip: 0,
+      };
+
+      const result = await handler(params);
+
+      // Should handle sorting even with null/undefined names
+      const parsedResult = JSON.parse(result.content[0].text);
+      expect(parsedResult).toHaveLength(3);
+    });
+
+    it("should handle non-Error exceptions in list_pull_requests_by_repo", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_repo);
+      if (!call) throw new Error("repo_list_pull_requests_by_repo tool not registered");
+      const [, , , handler] = call;
+
+      mockGetUserIdFromEmail.mockRejectedValue("String error"); // Non-Error exception
+
+      const params = {
+        repositoryId: "repo123",
+        created_by_user: "nonexistent@example.com",
+        status: "Active",
+        top: 100,
+        skip: 0,
+      };
+
+      const result = await handler(params);
+
+      expect(result.isError).toBe(true);
+      expect(result.content[0].text).toContain("Error finding user with email nonexistent@example.com: String error");
+    });
+
+    it("should handle non-Error exceptions in list_pull_requests_by_project", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_project);
+      if (!call) throw new Error("repo_list_pull_requests_by_project tool not registered");
+      const [, , , handler] = call;
+
+      mockGetUserIdFromEmail.mockRejectedValue("String error"); // Non-Error exception
+
+      const params = {
+        project: "test-project",
+        created_by_user: "nonexistent@example.com",
+        status: "Active",
+        top: 100,
+        skip: 0,
+      };
+
+      const result = await handler(params);
+
+      expect(result.isError).toBe(true);
+      expect(result.content[0].text).toContain("Error finding user with email nonexistent@example.com: String error");
+    });
+
+    it("should handle non-Error exceptions in list_pull_requests_by_commits", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.list_pull_requests_by_commits);
+      if (!call) throw new Error("repo_list_pull_requests_by_commits tool not registered");
+      const [, , , handler] = call;
+
+      mockGitApi.getPullRequestQuery.mockRejectedValue("String error"); // Non-Error exception
+
+      const params = {
+        project: "test-project",
+        repository: "test-repo",
+        commits: ["abc123", "def456"],
+        queryType: "LastMergeCommit",
+      };
+
+      const result = await handler(params);
+
+      expect(result.isError).toBe(true);
+      expect(result.content[0].text).toContain("Error querying pull requests by commits: String error");
+    });
+
+    it("should handle invalid rightFileEndOffset with rightFileEndLine in create_pull_request_thread", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.create_pull_request_thread);
+      if (!call) throw new Error("repo_create_pull_request_thread tool not registered");
+      const [, , , handler] = call;
+
+      const params = {
+        repositoryId: "repo123",
+        pullRequestId: 456,
+        content: "Test comment",
+        filePath: "/test/file.js",
+        rightFileStartLine: 5,
+        rightFileEndLine: 10,
+        rightFileEndOffset: 0, // Invalid end offset when end line is specified
+      };
+
+      await expect(handler(params)).rejects.toThrow("rightFileEndOffset must be greater than or equal to 1.");
+    });
+
+    it("should handle non-Error exceptions in search_commits", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.search_commits);
+      if (!call) throw new Error("repo_search_commits tool not registered");
+      const [, , , handler] = call;
+
+      mockGitApi.getCommits.mockRejectedValue("String error"); // Non-Error exception
+
+      const params = {
+        project: "test-project",
+        repository: "test-repo",
+        top: 10,
+        skip: 0,
+      };
+
+      const result = await handler(params);
+
+      expect(result.isError).toBe(true);
+      expect(result.content[0].text).toContain("Error searching commits: String error");
+    });
+
+    it("should handle valid rightFileEndOffset with rightFileEndLine in create_pull_request_thread", async () => {
+      configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider);
+
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === REPO_TOOLS.create_pull_request_thread);
+      if (!call) throw new Error("repo_create_pull_request_thread tool not registered");
+      const [, , , handler] = call;
+
+      const mockThread = { id: 1, status: CommentThreadStatus.Active };
+      mockGitApi.createThread.mockResolvedValue(mockThread);
+
+      const params = {
+        repositoryId: "repo123",
+        pullRequestId: 456,
+        content: "Test comment",
+        filePath: "/test/file.js",
+        rightFileStartLine: 5,
+        rightFileEndLine: 10,
+        rightFileEndOffset: 20, // Valid end offset with end line
+      };
+
+      const result = await handler(params);
+
+      expect(mockGitApi.createThread).toHaveBeenCalledWith(
+        expect.objectContaining({
+          threadContext: expect.objectContaining({
+            rightFileEnd: expect.objectContaining({
+              line: 10,
+              offset: 20,
+            }),
+          }),
+        }),
+        "repo123",
+        456,
+        undefined
+      );
+      expect(result.content[0].text).toBe(JSON.stringify(mockThread, null, 2));
+    });
+  });
 });
diff --git a/test/src/tools/wiki.test.ts b/test/src/tools/wiki.test.ts
index b0f48a7..428d1ab 100644
--- a/test/src/tools/wiki.test.ts
+++ b/test/src/tools/wiki.test.ts
@@ -459,6 +459,126 @@ describe("configureWikiTools", () => {
       expect(result.isError).toBe(true);
       expect(result.content[0].text).toContain("Error fetching wiki page content: Unknown error occurred");
     });
+
+    it("should retrieve content via URL with pagePath", async () => {
+      configureWikiTools(server, tokenProvider, connectionProvider);
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "wiki_get_page_content");
+      if (!call) throw new Error("wiki_get_page_content tool not registered");
+      const [, , , handler] = call;
+
+      const mockStream = {
+        setEncoding: jest.fn(),
+        on: function (event: string, cb: (chunk?: unknown) => void) {
+          if (event === "data") setImmediate(() => cb("url path content"));
+          if (event === "end") setImmediate(() => cb());
+          return this;
+        },
+      };
+      mockWikiApi.getPageText.mockResolvedValue(mockStream as unknown);
+
+      const url = "https://dev.azure.com/org/project/_wiki/wikis/myWiki?wikiVersion=GBmain&pagePath=%2FDocs%2FIntro";
+      const result = await handler({ url });
+
+      expect(mockWikiApi.getPageText).toHaveBeenCalledWith("project", "myWiki", "/Docs/Intro", undefined, undefined, true);
+      expect(result.content[0].text).toBe('"url path content"');
+    });
+
+    it("should retrieve content via URL with pageId (may fallback to root path)", async () => {
+      configureWikiTools(server, tokenProvider, connectionProvider);
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "wiki_get_page_content");
+      if (!call) throw new Error("wiki_get_page_content tool not registered");
+      const [, , , handler] = call;
+      // Ensure token is returned
+      (tokenProvider as jest.Mock).mockResolvedValueOnce({ token: "abc", expiresOnTimestamp: Date.now() + 10000 });
+      const mockStream = {
+        setEncoding: jest.fn(),
+        on: function (event: string, cb: (chunk?: unknown) => void) {
+          if (event === "data") setImmediate(() => cb("# Page Title\nBody"));
+          if (event === "end") setImmediate(() => cb());
+          return this;
+        },
+      };
+      mockWikiApi.getPageText.mockResolvedValue(mockStream as unknown);
+
+      // Mock fetch for REST page by id returning content
+      const mockFetch = jest.fn();
+      global.fetch = mockFetch as any;
+      mockFetch.mockResolvedValueOnce({
+        ok: true,
+        json: jest.fn().mockResolvedValue({ content: "# Page Title\nBody" }),
+      });
+
+      const url = "https://dev.azure.com/org/project/_wiki/wikis/myWiki/123/Page-Title";
+      const result = await handler({ url });
+
+      // Current implementation may fallback to root path stream retrieval
+      expect(mockWikiApi.getPageText).toHaveBeenCalledWith("project", "myWiki", "/", undefined, undefined, true);
+      // Content either direct or from stream JSON string wrapping
+      expect(result.content[0].text).toContain("Page Title");
+    });
+
+    it("should fallback to getPageText when REST call lacks content but returns path (root path fallback)", async () => {
+      configureWikiTools(server, tokenProvider, connectionProvider);
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "wiki_get_page_content");
+      if (!call) throw new Error("wiki_get_page_content tool not registered");
+      const [, , , handler] = call;
+      (tokenProvider as jest.Mock).mockResolvedValueOnce({ token: "abc", expiresOnTimestamp: Date.now() + 10000 });
+
+      const mockFetch = jest.fn();
+      global.fetch = mockFetch as any;
+      mockFetch.mockResolvedValueOnce({
+        ok: true,
+        json: jest.fn().mockResolvedValue({ path: "/Some/Page" }),
+      });
+
+      const mockStream = {
+        setEncoding: jest.fn(),
+        on: function (event: string, cb: (chunk?: unknown) => void) {
+          if (event === "data") setImmediate(() => cb("fallback content"));
+          if (event === "end") setImmediate(() => cb());
+          return this;
+        },
+      };
+      mockWikiApi.getPageText.mockResolvedValue(mockStream as unknown);
+
+      const url = "https://dev.azure.com/org/project/_wiki/wikis/myWiki/999/Some-Page";
+      const result = await handler({ url });
+
+      // Implementation currently falls back to root path if path not resolved prior to fallback
+      expect(mockWikiApi.getPageText).toHaveBeenCalledWith("project", "myWiki", "/", undefined, undefined, true);
+      expect(result.content[0].text).toBe('"fallback content"');
+    });
+
+    it("should error when both url and wikiIdentifier provided", async () => {
+      configureWikiTools(server, tokenProvider, connectionProvider);
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "wiki_get_page_content");
+      if (!call) throw new Error("wiki_get_page_content tool not registered");
+      const [, , , handler] = call;
+      const result = await handler({ url: "https://dev.azure.com/org/project/_wiki/wikis/wiki1?pagePath=%2FHome", wikiIdentifier: "wiki1", project: "project" });
+      expect(result.isError).toBe(true);
+      expect(result.content[0].text).toContain("Provide either 'url' OR 'wikiIdentifier'");
+    });
+
+    it("should error when neither url nor identifiers provided", async () => {
+      configureWikiTools(server, tokenProvider, connectionProvider);
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "wiki_get_page_content");
+      if (!call) throw new Error("wiki_get_page_content tool not registered");
+      const [, , , handler] = call;
+      const result = await handler({ path: "/Home" });
+      expect(result.isError).toBe(true);
+      expect(result.content[0].text).toContain("You must provide either 'url' OR both 'wikiIdentifier' and 'project'");
+    });
+
+    it("should error on malformed wiki URL", async () => {
+      configureWikiTools(server, tokenProvider, connectionProvider);
+      const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "wiki_get_page_content");
+      if (!call) throw new Error("wiki_get_page_content tool not registered");
+      const [, , , handler] = call;
+
+      const result = await handler({ url: "https://dev.azure.com/org/project/notwiki/wikis/wiki1?pagePath=%2FHome" });
+      expect(result.isError).toBe(true);
+      expect(result.content[0].text).toContain("Error fetching wiki page content: URL does not match expected wiki pattern");
+    });
   });
 
   describe("create_or_update_page tool", () => {
diff --git a/test/src/utils.test.ts b/test/src/utils.test.ts
index 08f9e20..4712d49 100644
--- a/test/src/utils.test.ts
+++ b/test/src/utils.test.ts
@@ -2,7 +2,7 @@
 // Licensed under the MIT License.
 
 import { AlertType, AlertValidityStatus, Confidence, Severity, State } from "azure-devops-node-api/interfaces/AlertInterfaces";
-import { createEnumMapping, mapStringArrayToEnum, mapStringToEnum } from "../../src/utils";
+import { createEnumMapping, getEnumKeys, mapStringArrayToEnum, mapStringToEnum, safeEnumConvert } from "../../src/utils";
 
 describe("utils", () => {
   describe("createEnumMapping", () => {
@@ -344,4 +344,90 @@ describe("utils", () => {
       expect(mappedStates).toEqual([State.Active, State.Dismissed, State.Fixed]);
     });
   });
+
+  describe("getEnumKeys", () => {
+    it("should return string keys from a numeric enum", () => {
+      const keys = getEnumKeys(AlertType);
+      expect(keys).toEqual(["Unknown", "Dependency", "Secret", "Code", "License"]);
+    });
+
+    it("should filter out numeric keys", () => {
+      // Create a mock enum with numeric keys (similar to TypeScript's reverse mappings)
+      const mockEnum = {
+        A: 0,
+        B: 1,
+        C: 2,
+        0: "A", // Reverse mapping
+        1: "B", // Reverse mapping
+        2: "C", // Reverse mapping
+      };
+
+      const keys = getEnumKeys(mockEnum);
+      expect(keys).toEqual(["A", "B", "C"]);
+    });
+
+    it("should handle empty enum object", () => {
+      const keys = getEnumKeys({});
+      expect(keys).toEqual([]);
+    });
+
+    it("should work with different enum types", () => {
+      expect(getEnumKeys(State)).toEqual(["Unknown", "Active", "Dismissed", "Fixed", "AutoDismissed"]);
+      expect(getEnumKeys(Severity)).toEqual(["Low", "Medium", "High", "Critical", "Note", "Warning", "Error", "Undefined"]);
+      expect(getEnumKeys(Confidence)).toEqual(["High", "Other"]);
+    });
+  });
+
+  describe("safeEnumConvert", () => {
+    it("should convert valid enum string keys to values", () => {
+      expect(safeEnumConvert(AlertType, "Code")).toBe(AlertType.Code);
+      expect(safeEnumConvert(AlertType, "Secret")).toBe(AlertType.Secret);
+      expect(safeEnumConvert(AlertType, "Unknown")).toBe(AlertType.Unknown);
+    });
+
+    it("should return undefined for invalid keys", () => {
+      expect(safeEnumConvert(AlertType, "InvalidKey")).toBeUndefined();
+      expect(safeEnumConvert(AlertType, "code")).toBeUndefined(); // Case sensitive
+      expect(safeEnumConvert(AlertType, "SECRET")).toBeUndefined(); // Case sensitive
+    });
+
+    it("should handle undefined input", () => {
+      expect(safeEnumConvert(AlertType, undefined)).toBeUndefined();
+    });
+
+    it("should handle empty string", () => {
+      expect(safeEnumConvert(AlertType, "")).toBeUndefined();
+    });
+
+    it("should work with numeric enums", () => {
+      expect(safeEnumConvert(State, "Active")).toBe(State.Active);
+      expect(safeEnumConvert(State, "Dismissed")).toBe(State.Dismissed);
+      expect(safeEnumConvert(State, "Fixed")).toBe(State.Fixed);
+    });
+
+    it("should respect case sensitivity", () => {
+      // This differs from mapStringToEnum which is case-insensitive
+      expect(safeEnumConvert(State, "active")).toBeUndefined();
+      expect(safeEnumConvert(State, "ACTIVE")).toBeUndefined();
+      expect(safeEnumConvert(State, "Active")).toBe(State.Active);
+    });
+
+    it("should work with empty enum", () => {
+      expect(safeEnumConvert({}, "anyKey")).toBeUndefined();
+    });
+
+    it("should validate against enum keys", () => {
+      // Create a mock enum with numeric keys (similar to TypeScript's reverse mappings)
+      const mockEnum = {
+        A: 0,
+        B: 1,
+        0: "A", // Reverse mapping
+        1: "B", // Reverse mapping
+      };
+
+      expect(safeEnumConvert(mockEnum, "A")).toBe(0);
+      expect(safeEnumConvert(mockEnum, "B")).toBe(1);
+      expect(safeEnumConvert(mockEnum, "0")).toBeUndefined(); // Numeric strings aren't valid keys
+    });
+  });
 });