Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/tools/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { registerMetadataTools } from "./metadata.js";
import { registerModuleIssueTools } from "./module-issues.js";
import { registerModuleTools } from "./modules.js";
import { registerProjectTools } from "./projects.js";
import { registerSearchIssueTools } from "./search-issues.js";
import { registerUserTools } from "./user.js";
import { registerWorkLogTools } from "./work-log.js";

Expand All @@ -15,6 +16,7 @@ export const registerTools = (server: McpServer) => {
registerUserTools(server);

registerProjectTools(server);
registerSearchIssueTools(server);
registerModuleTools(server);
registerModuleIssueTools(server);
registerIssueTools(server);
Expand Down
38 changes: 38 additions & 0 deletions src/tools/search-issues.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

import { makePlaneRequest } from "../common/request-helper.js";

export const registerSearchIssueTools = (server: McpServer): void => {
server.tool(
"search_issues",
"Use this to search issues by text query. This requests project_id as uuid parameter. If you have a readable identifier for project, you can use the get_projects tool to get the project_id from it",
{
limit: z.number().describe("The number of issues to return").optional(),
project_id: z.string().describe("The uuid identifier of the project to search issues for").optional(),
search: z.string().describe("The search query"),
workspace_search: z.boolean().describe("Whether to search across all projects in the workspace").optional(),
},
async ({ limit, project_id, search, workspace_search }) => {
// send the params if not null as query string
const queryParams = new URLSearchParams();
if (limit) queryParams.set("limit", limit.toString());
if (project_id) queryParams.set("project_id", project_id);
if (workspace_search) queryParams.set("workspace_search", workspace_search.toString());
if (search) queryParams.set("search", search);
Comment on lines +19 to +22
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Don’t gate on truthiness; include false/0 and avoid dropping valid values.

Using truthy checks drops limit=0 and workspace_search=false. Prefer explicit undefined checks and always send the required search param.

-      if (limit) queryParams.set("limit", limit.toString());
+      if (limit !== undefined) queryParams.set("limit", String(limit));
       if (project_id) queryParams.set("project_id", project_id);
-      if (workspace_search) queryParams.set("workspace_search", workspace_search.toString());
-      if (search) queryParams.set("search", search);
+      if (workspace_search !== undefined) queryParams.set("workspace_search", String(workspace_search));
+      queryParams.set("search", search);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (limit) queryParams.set("limit", limit.toString());
if (project_id) queryParams.set("project_id", project_id);
if (workspace_search) queryParams.set("workspace_search", workspace_search.toString());
if (search) queryParams.set("search", search);
if (limit !== undefined) queryParams.set("limit", String(limit));
if (project_id) queryParams.set("project_id", project_id);
if (workspace_search !== undefined) queryParams.set("workspace_search", String(workspace_search));
queryParams.set("search", search);
🤖 Prompt for AI Agents
In src/tools/search-issues.ts around lines 19 to 22, the current truthy checks
drop valid falsy values like limit=0 and workspace_search=false and may omit the
required search param; change the guards to explicit undefined/null checks
(e.g., check for !== undefined && !== null) so limit and workspace_search are
set even when falsy, and always set the search query parameter (without gating
on truthiness) to ensure it is sent.


const response = await makePlaneRequest(
"GET",
`workspaces/${process.env.PLANE_WORKSPACE_SLUG}/issues/search/?${queryParams.toString()}`
);
Comment on lines +24 to +27
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Guard missing workspace slug and URL-encode it.

Without PLANE_WORKSPACE_SLUG this hits workspaces/undefined/…, yielding confusing failures. Also encode the slug for safety.

-      const response = await makePlaneRequest(
-        "GET",
-        `workspaces/${process.env.PLANE_WORKSPACE_SLUG}/issues/search/?${queryParams.toString()}`
-      );
+      const slug = process.env.PLANE_WORKSPACE_SLUG;
+      if (!slug) {
+        throw new Error("Environment variable PLANE_WORKSPACE_SLUG is required for search_issues.");
+      }
+      const path = `workspaces/${encodeURIComponent(slug)}/issues/search/?${queryParams.toString()}`;
+      const response = await makePlaneRequest("GET", path);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const response = await makePlaneRequest(
"GET",
`workspaces/${process.env.PLANE_WORKSPACE_SLUG}/issues/search/?${queryParams.toString()}`
);
const slug = process.env.PLANE_WORKSPACE_SLUG;
if (!slug) {
throw new Error("Environment variable PLANE_WORKSPACE_SLUG is required for search_issues.");
}
const path = `workspaces/${encodeURIComponent(slug)}/issues/search/?${queryParams.toString()}`;
const response = await makePlaneRequest("GET", path);
🤖 Prompt for AI Agents
In src/tools/search-issues.ts around lines 24 to 27, the code uses
process.env.PLANE_WORKSPACE_SLUG directly when building the request path which
can produce confusing failures if the env var is missing and is unsafe for
special characters; add a guard that checks for the presence of
PLANE_WORKSPACE_SLUG and either throw a clear Error or return a rejected promise
if it's undefined/empty, and use
encodeURIComponent(process.env.PLANE_WORKSPACE_SLUG) when interpolating the slug
into the URL so the workspace slug is URL-encoded.

return {
content: [
{
type: "text",
text: JSON.stringify(response, null, 2),
},
],
};
}
);
};