Skip to main content
Glama

List Issues

list_issues
Read-onlyIdempotent

Retrieve and filter MantisBT bug tracker issues with pagination, field selection, and date-based queries to manage project workflows efficiently.

Instructions

List MantisBT issues with optional filtering. Returns a paginated list of issues. Use the "select" parameter to limit returned fields and reduce response size significantly.

Note: "assigned_to", "reporter_id", "status", and date filters are applied client-side (the MantisBT REST API does not support these as server-side filters). When any of these filters are active the tool automatically fetches multiple pages internally until enough matching results are found (up to 500 issues scanned). The "page" and "page_size" parameters refer to the resulting filtered list.

Tip for date queries: fetching with select="id,updated_at,created_at" plus a date filter is very compact and efficient.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
project_idNoFilter by project ID
pageNoPage number (default: 1)
page_sizeNoIssues per page (default: 50, max: 50)
assigned_toNoFilter by handler/assignee user ID
reporter_idNoFilter by reporter user ID
filter_idNoUse a saved MantisBT filter ID
sortNoSort field (e.g. "last_updated", "id")
directionNoSort direction
selectNoComma-separated list of fields to include in the response (server-side projection). Significantly reduces response size. Example: "id,summary,status,priority,handler,updated_at"
statusNoFilter issues by status name (e.g. "new", "feedback", "acknowledged", "confirmed", "assigned", "resolved", "closed") or use "open" as shorthand for all statuses with id < 80 (i.e. not yet resolved or closed). Applied client-side after fetching — when combined with pagination, a page may contain fewer results than page_size.
updated_afterNoISO-8601 timestamp — only return issues updated after this date (exclusive). Example: "2026-03-25T00:00:00Z"
updated_beforeNoISO-8601 timestamp — only return issues updated before this date (exclusive). Example: "2026-03-28T00:00:00Z"
created_afterNoISO-8601 timestamp — only return issues created after this date (exclusive). Example: "2026-03-01T00:00:00Z"
created_beforeNoISO-8601 timestamp — only return issues created before this date (exclusive). Example: "2026-03-15T00:00:00Z"

Implementation Reference

  • The handler function for 'list_issues' that performs MantisBT issue fetching and client-side filtering/pagination.
    async ({ project_id, page, page_size, assigned_to, reporter_id, filter_id, sort, direction, select, status, updated_after, updated_before, created_after, created_before }) => {
      try {
        const baseParams: Record<string, string | number | boolean | undefined> = {
          project_id,
          assigned_to,
          reporter_id,
          filter_id,
          sort,
          direction,
          select,
        };
    
        const dateFilter: DateFilter = { updated_after, updated_before, created_after, created_before };
        const needsClientFilter = status !== undefined || assigned_to !== undefined || reporter_id !== undefined || hasDateFilter(dateFilter);
    
        if (!needsClientFilter) {
          // No client-side filtering — single API call, pass pagination as-is
          const result = await client.get<MantisPaginatedIssues>('issues', { ...baseParams, page, page_size });
          return {
            content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
          };
        }
    
        // Client-side filtering active: scan multiple API pages until we have
        // enough matching results for the requested logical page.
        const API_PAGE_SIZE = 50; // always fetch max to minimise round-trips
        const MAX_API_PAGES = 10; // hard cap: scan at most 500 issues
        const neededTotal = page * page_size; // need this many matches to serve page N
    
        const matching: MantisIssue[] = [];
        let serverPage = 1;
        let hasMore = true;
    
        const statusLower = status?.toLowerCase();
        const statusId = status ? resolveEnumId('status', status) : undefined;
        // Pre-parse date thresholds once — avoids repeated new Date() inside the scan loop
        const updatedAfterMs = updated_after ? new Date(updated_after).getTime() : undefined;
    
        while (matching.length < neededTotal && serverPage <= MAX_API_PAGES && hasMore) {
          const batch = await client.get<MantisPaginatedIssues>('issues', {
            ...baseParams,
            page: serverPage,
            page_size: API_PAGE_SIZE,
          });
    
          const issues = batch.issues ?? [];
          hasMore = issues.length === API_PAGE_SIZE;
    
          let stopAfterBatch = false;
          for (const issue of issues) {
            if (statusLower) {
              if (!issue.status) continue;
              if (statusLower === 'open') {
                if ((issue.status.id ?? 0) >= MANTIS_RESOLVED_STATUS_ID) continue;
              } else if (statusId !== undefined) {
                if (issue.status.id !== statusId) continue;
              } else if (issue.status.name?.toLowerCase() !== statusLower) {
                continue;
              }
            }
            if (assigned_to !== undefined && issue.handler?.id !== assigned_to) continue;
            if (reporter_id !== undefined && issue.reporter?.id !== reporter_id) continue;
            if (!matchesDateFilter(issue, dateFilter)) {
              // MantisBT returns results newest-first. Once updated_at drops below
              // updated_after, all subsequent pages are guaranteed to be older too.
              // Finish the current batch first (items within it may still be newer),
              // then stop fetching further pages.
              if (updatedAfterMs && issue.updated_at && new Date(issue.updated_at).getTime() <= updatedAfterMs) {
                stopAfterBatch = true;
              }
              continue;
            }
            matching.push(issue);
          }
    
          if (stopAfterBatch) break;
          serverPage++;
        }
    
        const start = (page - 1) * page_size;
        return {
          content: [{
            type: 'text',
            text: JSON.stringify({ issues: matching.slice(start, start + page_size) }, null, 2),
          }],
        };
      } catch (error) {
        const msg = error instanceof Error ? error.message : String(error);
        return { content: [{ type: 'text', text: errorText(msg) }], isError: true };
      }
    }
  • Tool registration for 'list_issues' along with its input schema and description.
    server.registerTool(
      'list_issues',
      {
        title: 'List Issues',
        description: 'List MantisBT issues with optional filtering. Returns a paginated list of issues. Use the "select" parameter to limit returned fields and reduce response size significantly.\n\nNote: "assigned_to", "reporter_id", "status", and date filters are applied client-side (the MantisBT REST API does not support these as server-side filters). When any of these filters are active the tool automatically fetches multiple pages internally until enough matching results are found (up to 500 issues scanned). The "page" and "page_size" parameters refer to the resulting filtered list.\n\nTip for date queries: fetching with select="id,updated_at,created_at" plus a date filter is very compact and efficient.',
        inputSchema: z.object({
          project_id: z.coerce.number().int().positive().optional().describe('Filter by project ID'),
          page: z.coerce.number().int().positive().default(1).describe('Page number (default: 1)'),
          page_size: z.coerce.number().int().min(1).max(50).default(50).describe('Issues per page (default: 50, max: 50)'),
          assigned_to: z.coerce.number().int().positive().optional().describe('Filter by handler/assignee user ID'),
          reporter_id: z.coerce.number().int().positive().optional().describe('Filter by reporter user ID'),
          filter_id: z.coerce.number().int().positive().optional().describe('Use a saved MantisBT filter ID'),
          sort: z.string().optional().describe('Sort field (e.g. "last_updated", "id")'),
          direction: z.enum(['ASC', 'DESC']).optional().describe('Sort direction'),
          select: z.string().optional().describe('Comma-separated list of fields to include in the response (server-side projection). Significantly reduces response size. Example: "id,summary,status,priority,handler,updated_at"'),
          status: z.string().optional().describe('Filter issues by status name (e.g. "new", "feedback", "acknowledged", "confirmed", "assigned", "resolved", "closed") or use "open" as shorthand for all statuses with id < 80 (i.e. not yet resolved or closed). Applied client-side after fetching — when combined with pagination, a page may contain fewer results than page_size.'),
          ...dateFilterSchema,
        }),
        annotations: {
          readOnlyHint: true,
          destructiveHint: false,
          idempotentHint: true,
        },
      },
Behavior5/5

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

Excellent disclosure beyond annotations: explicitly warns that assigned_to, reporter_id, status, and date filters are client-side (not server-side), explains the internal multi-page fetching behavior (up to 500 issues scanned), and clarifies that page/page_size refer to the filtered result set, not the raw API pages.

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

Conciseness5/5

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

Well-structured with clear progression: basic function → critical behavioral note → optimization tip. No wasted words; every sentence provides actionable guidance or warns about non-obvious behavior.

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

Completeness5/5

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

Comprehensive for a 14-parameter list operation. Without an output schema, the description adequately explains the return behavior (paginated list) and thoroughly covers the complex client-side filtering mechanics that would otherwise surprise users.

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

Parameters5/5

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

Despite 100% schema coverage (baseline 3), the description adds crucial semantic context: explains the performance impact of 'select', provides concrete syntax examples, and details how client-side filters interact with pagination parameters—essential for correct invocation.

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?

States specific verb (List) + resource (MantisBT issues) + capability (optional filtering/pagination). However, it does not distinguish from sibling 'get_issues', which could confuse agents about when to use list-based querying versus direct retrieval.

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

Usage Guidelines4/5

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

Provides clear context on HOW to use the tool effectively—including the 'select' parameter recommendation, date query optimization tips, and warnings about client-side filtering behavior. Lacks explicit comparison to sibling alternatives (e.g., when to use get_issue vs list_issues).

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

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/dpesch/mantisbt-mcp-server'

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