Skip to main content
Glama
Codeshark-NET

ClimateTriage MCP Server

search_climate_triage_issues

Find open source issues related to climate change and sustainability projects. Filter by category, programming language, or keyword to discover opportunities to contribute. Supports pagination and sorting for easy access to relevant issues.

Instructions

Searches for open source issues related to climate change, sustainability and more. Use this tool to find opportunities to contribute to projects addressing climate challenges, explore issues in specific programming languages, or discover projects in various sustainability categories. Returns information about issues including project details, descriptions, and links. Supports filtering, pagination, and sorting to help find relevant issues.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
categoryNoFilter issues by project category
keywordNoFilter issues by project keyword (e.g., 'bug', 'help wanted', 'enhancement')
languageNoFilter issues by programming language
orderNoSort order (asc or desc) (default: desc for most recent first)desc
pageNoPagination page number (starts at 1)
per_pageNoNumber of records per page (default: 10)
sortNoField to sort by (default: created_at). Use created_at for most recent issues first.created_at

Implementation Reference

  • index.ts:15-223 (registration)
    Registration of the 'search_climate_triage_issues' tool using server.addTool, including name, description, parameters schema, and execute handler.
    server.addTool({
      name: "search_climate_triage_issues",
      description:
        "Searches for open source issues related to climate change, sustainability and more. " +
        "Use this tool to find opportunities to contribute to projects addressing climate challenges, " +
        "explore issues in specific programming languages, or discover projects in various sustainability categories. " +
        "Returns information about issues including project details, descriptions, and links. " +
        "Supports filtering, pagination, and sorting to help find relevant issues.",
      parameters: z.object({
        category: z
          .enum([
            "Climate Change",
            "Energy Systems",
            "Emissions",
            "Consumption",
            "Biosphere",
            "Hydrosphere",
            "Sustainable Development",
          ])
          .describe("Filter issues by project category")
          .optional(),
        language: z
          .enum([
            "JavaScript",
            "TypeScript",
            "Python",
            "Java",
            "C#",
            "C++",
            "C",
            "Ruby",
            "Go",
            "Rust",
            "Swift",
            "Kotlin",
            "PHP",
            "HTML",
            "CSS",
            "Shell",
            "Dart",
            "Scala",
            "R",
            "Elixir",
            "Clojure",
          ])
          .describe("Filter issues by programming language")
          .optional(),
        keyword: z
          .string()
          .describe(
            "Filter issues by project keyword (e.g., 'bug', 'help wanted', 'enhancement')"
          )
          .optional(),
        page: z
          .number()
          .int()
          .min(1)
          .describe("Pagination page number (starts at 1)")
          .optional(),
        per_page: z
          .number()
          .int()
          .min(1)
          .default(10)
          .describe("Number of records per page (default: 10)")
          .optional(),
        sort: z
          .enum(["created_at", "updated_at", "stars"])
          .default("created_at")
          .describe(
            "Field to sort by (default: created_at). Use created_at for most recent issues first."
          )
          .optional(),
        order: z
          .enum(["asc", "desc"])
          .default("desc")
          .describe(
            "Sort order (asc or desc) (default: desc for most recent first)"
          )
          .optional(),
      }),
      execute: async (args, { log }) => {
        try {
          log.info("Searching for climate issues", {
            category: args.category,
            language: args.language,
            keyword: args.keyword,
            page: args.page,
            per_page: args.per_page,
            sort: args.sort,
            order: args.order,
          });
    
          const params = new URLSearchParams();
    
          if (args.category) {
            params.append("category", args.category);
          }
    
          if (args.language) {
            params.append("language", args.language);
          }
    
          if (args.keyword) {
            params.append("keyword", args.keyword);
          }
    
          if (args.page) {
            params.append("page", args.page.toString());
          }
    
          if (args.per_page !== undefined) {
            params.append("per_page", args.per_page.toString());
          }
    
          if (args.sort) {
            params.append("sort", args.sort);
          }
    
          if (args.order) {
            params.append("order", args.order);
          }
    
          const response = await fetch(
            `${API_BASE_URL}/issues?${params.toString()}`
          );
    
          if (!response.ok) {
            throw new UserError(
              `Failed to search climate issues: ${response.statusText} (${response.status})`
            );
          }
    
          const data = await response.json();
    
          // Transform the data to include only necessary fields
          const transformedData = Array.isArray(data)
            ? data.map((issue) => transformIssue(issue))
            : [];
    
          log.info("Climate issues search completed", {
            resultsCount: transformedData.length,
          });
    
          // Get pagination info
          const totalCount =
            parseInt(response.headers.get("X-Total-Count") || "0", 10) ||
            transformedData.length;
          const currentPage = args.page || 1;
          const perPage = args.per_page || 10;
          const totalPages = Math.ceil(totalCount / perPage);
    
          // Format the results in Markdown for better LLM presentation
          // Update the formatting in your execute method
          const formattedResults = transformedData
            .map((issue, index) => {
              return `
              ISSUE #${index + 1}: "${issue.title}"
              ISSUE LINK: ${issue.url}
              ISSUE CREATED: ${formatDate(issue.created_at)}
              ISSUE STATE: ${issue.state}
              ISSUE LABELS: ${issue.labels.join(", ") || "None"}
    
              BELONGS TO PROJECT: "${issue.project.name}"
              PROJECT LINK: ${issue.project.repository_url}
              PROJECT LANGUAGE: ${issue.project.language || "Unknown"}
              PROJECT CATEGORY: ${issue.project.category || "Uncategorized"}
              PROJECT STARS: ${issue.project.stars}
    
              ${issue.body ? `ISSUE DESCRIPTION: ${issue.body}\n` : ""}
              `;
            })
            .join("\n------------------------------\n");
    
          // Add pagination information
          let paginationInfo = "";
          if (transformedData.length > 0) {
            paginationInfo = `\n\n---\n\nShowing page ${currentPage} of ${totalPages} (${transformedData.length} of ${totalCount} total results)`;
    
            if (currentPage < totalPages) {
              paginationInfo += `\nUse \`page: ${
                currentPage + 1
              }\` to see more results.`;
            }
          }
    
          return {
            content: [
              {
                type: "text",
                text:
                  transformedData.length > 0
                    ? `# Found ${totalCount} Climate Issues\n\n${formattedResults}${paginationInfo}`
                    : "No issues found matching your criteria. Try adjusting your search parameters.",
              },
            ],
          };
        } catch (error) {
          if (error instanceof UserError) {
            throw error;
          }
    
          const errorMessage =
            error instanceof Error ? error.message : String(error);
    
          throw new UserError(`Error searching climate issues: ${errorMessage}`);
        }
      },
    });
  • index.ts:96-222 (handler)
    The execute function that implements the core logic: builds query params, fetches from API, transforms data, formats output in Markdown with pagination.
    execute: async (args, { log }) => {
      try {
        log.info("Searching for climate issues", {
          category: args.category,
          language: args.language,
          keyword: args.keyword,
          page: args.page,
          per_page: args.per_page,
          sort: args.sort,
          order: args.order,
        });
    
        const params = new URLSearchParams();
    
        if (args.category) {
          params.append("category", args.category);
        }
    
        if (args.language) {
          params.append("language", args.language);
        }
    
        if (args.keyword) {
          params.append("keyword", args.keyword);
        }
    
        if (args.page) {
          params.append("page", args.page.toString());
        }
    
        if (args.per_page !== undefined) {
          params.append("per_page", args.per_page.toString());
        }
    
        if (args.sort) {
          params.append("sort", args.sort);
        }
    
        if (args.order) {
          params.append("order", args.order);
        }
    
        const response = await fetch(
          `${API_BASE_URL}/issues?${params.toString()}`
        );
    
        if (!response.ok) {
          throw new UserError(
            `Failed to search climate issues: ${response.statusText} (${response.status})`
          );
        }
    
        const data = await response.json();
    
        // Transform the data to include only necessary fields
        const transformedData = Array.isArray(data)
          ? data.map((issue) => transformIssue(issue))
          : [];
    
        log.info("Climate issues search completed", {
          resultsCount: transformedData.length,
        });
    
        // Get pagination info
        const totalCount =
          parseInt(response.headers.get("X-Total-Count") || "0", 10) ||
          transformedData.length;
        const currentPage = args.page || 1;
        const perPage = args.per_page || 10;
        const totalPages = Math.ceil(totalCount / perPage);
    
        // Format the results in Markdown for better LLM presentation
        // Update the formatting in your execute method
        const formattedResults = transformedData
          .map((issue, index) => {
            return `
            ISSUE #${index + 1}: "${issue.title}"
            ISSUE LINK: ${issue.url}
            ISSUE CREATED: ${formatDate(issue.created_at)}
            ISSUE STATE: ${issue.state}
            ISSUE LABELS: ${issue.labels.join(", ") || "None"}
    
            BELONGS TO PROJECT: "${issue.project.name}"
            PROJECT LINK: ${issue.project.repository_url}
            PROJECT LANGUAGE: ${issue.project.language || "Unknown"}
            PROJECT CATEGORY: ${issue.project.category || "Uncategorized"}
            PROJECT STARS: ${issue.project.stars}
    
            ${issue.body ? `ISSUE DESCRIPTION: ${issue.body}\n` : ""}
            `;
          })
          .join("\n------------------------------\n");
    
        // Add pagination information
        let paginationInfo = "";
        if (transformedData.length > 0) {
          paginationInfo = `\n\n---\n\nShowing page ${currentPage} of ${totalPages} (${transformedData.length} of ${totalCount} total results)`;
    
          if (currentPage < totalPages) {
            paginationInfo += `\nUse \`page: ${
              currentPage + 1
            }\` to see more results.`;
          }
        }
    
        return {
          content: [
            {
              type: "text",
              text:
                transformedData.length > 0
                  ? `# Found ${totalCount} Climate Issues\n\n${formattedResults}${paginationInfo}`
                  : "No issues found matching your criteria. Try adjusting your search parameters.",
            },
          ],
        };
      } catch (error) {
        if (error instanceof UserError) {
          throw error;
        }
    
        const errorMessage =
          error instanceof Error ? error.message : String(error);
    
        throw new UserError(`Error searching climate issues: ${errorMessage}`);
      }
    },
  • Zod schema defining input parameters for category, language, keyword, pagination, sorting.
    parameters: z.object({
      category: z
        .enum([
          "Climate Change",
          "Energy Systems",
          "Emissions",
          "Consumption",
          "Biosphere",
          "Hydrosphere",
          "Sustainable Development",
        ])
        .describe("Filter issues by project category")
        .optional(),
      language: z
        .enum([
          "JavaScript",
          "TypeScript",
          "Python",
          "Java",
          "C#",
          "C++",
          "C",
          "Ruby",
          "Go",
          "Rust",
          "Swift",
          "Kotlin",
          "PHP",
          "HTML",
          "CSS",
          "Shell",
          "Dart",
          "Scala",
          "R",
          "Elixir",
          "Clojure",
        ])
        .describe("Filter issues by programming language")
        .optional(),
      keyword: z
        .string()
        .describe(
          "Filter issues by project keyword (e.g., 'bug', 'help wanted', 'enhancement')"
        )
        .optional(),
      page: z
        .number()
        .int()
        .min(1)
        .describe("Pagination page number (starts at 1)")
        .optional(),
      per_page: z
        .number()
        .int()
        .min(1)
        .default(10)
        .describe("Number of records per page (default: 10)")
        .optional(),
      sort: z
        .enum(["created_at", "updated_at", "stars"])
        .default("created_at")
        .describe(
          "Field to sort by (default: created_at). Use created_at for most recent issues first."
        )
        .optional(),
      order: z
        .enum(["asc", "desc"])
        .default("desc")
        .describe(
          "Sort order (asc or desc) (default: desc for most recent first)"
        )
        .optional(),
    }),
  • Helper function to transform raw API issue data into a standardized format, extracting labels, truncating body, nesting project info.
    function transformIssue(issue: any) {
      // Extract labels as strings
      const labels = Array.isArray(issue.labels)
        ? issue.labels.map((label: any) =>
            typeof label === "string" ? label : label.name || label.toString()
          )
        : [];
    
      return {
        id: issue.uuid,
        number: issue.number,
        title: issue.title,
        state: issue.state,
        author: issue.user?.login || issue.user?.name || "anonymous",
        labels: labels,
        comments_count: issue.comments_count || 0,
        created_at: issue.created_at,
        updated_at: issue.updated_at,
        url: issue.html_url,
        body: issue.body
          ? issue.body.substring(0, 200) + (issue.body.length > 200 ? "..." : "")
          : "",
        project: {
          name: issue.project?.name || "Unknown Project",
          description: issue.project?.description || "",
          language: issue.project?.language || "",
          category: issue.project?.category || "",
          stars: issue.project?.repository?.stargazers_count || 0,
          owner:
            issue.project?.repository?.owner?.login ||
            issue.project?.repository?.owner ||
            "unknown",
          repository_url:
            issue.project?.repository?.html_url || issue.project?.html_url || "",
        },
      };
    }
  • Helper function to format issue created_at dates into readable locale format.
    function formatDate(dateString: string | undefined): string {
      if (!dateString) return "Unknown date";
    
      try {
        const date = new Date(dateString);
        return date.toLocaleDateString(undefined, {
          year: "numeric",
          month: "short",
          day: "numeric",
        });
      } catch (e) {
        return dateString;
      }
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions that the tool 'returns information about issues including project details, descriptions, and links' and 'supports filtering, pagination, and sorting,' which adds useful context about output and capabilities. However, it doesn't cover critical aspects like rate limits, authentication needs, error handling, or data freshness, leaving gaps in behavioral understanding.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized and front-loaded, starting with the core purpose and following with usage examples and capabilities. Each sentence adds value, with no redundant information. However, it could be slightly more streamlined by integrating the capabilities into the initial statement.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (7 parameters, no annotations, no output schema), the description is moderately complete. It covers the purpose, output content, and supported features but lacks details on response format, error cases, or example outputs. Without an output schema, the agent must infer return values from the description alone, which is insufficient for full operational clarity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 100% description coverage, so the baseline is 3. The description adds minimal parameter semantics beyond the schema, mentioning filtering, pagination, and sorting generically but not explaining specific parameter interactions or use cases. It doesn't compensate for schema gaps because there are none, but also doesn't enhance parameter understanding significantly.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool searches for open source issues related to climate change and sustainability, specifying the resource (issues) and purpose (finding opportunities to contribute). It distinguishes the domain (climate/sustainability) but doesn't differentiate from siblings since none exist, making it clear but not fully optimized for sibling comparison.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides implied usage contexts ('to find opportunities to contribute,' 'explore issues,' 'discover projects') but lacks explicit guidance on when to use this tool versus alternatives. No exclusions or prerequisites are mentioned, leaving the agent to infer appropriate scenarios without clear boundaries.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Codeshark-NET/climate-triage-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server