get_issue_fields
Retrieve valid field names for MantisBT issue queries to ensure API requests target only existing server fields.
Instructions
Return all field names that are valid for the "select" parameter of list_issues and get_issue.
Fields are discovered by fetching a sample issue from MantisBT (which reflects the server's active configuration — e.g. whether eta, projection, or profile fields are enabled) and merging the result with fields that MantisBT omits when empty (notes, attachments, relationships, etc.). The result is cached with the same TTL as the metadata cache.
Use this tool before constructing a "select" string to ensure you only request fields that exist on this server.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| project_id | No | Optional project ID to scope the sample issue fetch |
Implementation Reference
- src/tools/metadata.ts:240-292 (registration)The tool 'get_issue_fields' is registered here. The handler logic retrieves fields by fetching a sample issue or using a fallback list, then caches the result.
server.registerTool( 'get_issue_fields', { title: 'Get Issue Fields', description: `Return all field names that are valid for the "select" parameter of list_issues and get_issue. Fields are discovered by fetching a sample issue from MantisBT (which reflects the server's active configuration — e.g. whether eta, projection, or profile fields are enabled) and merging the result with fields that MantisBT omits when empty (notes, attachments, relationships, etc.). The result is cached with the same TTL as the metadata cache. Use this tool before constructing a "select" string to ensure you only request fields that exist on this server.`, inputSchema: z.object({ project_id: z.coerce.number().int().positive().optional().describe('Optional project ID to scope the sample issue fetch'), }), annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, }, }, async ({ project_id }) => { try { const cached = await cache.loadIssueFields(); if (cached) { return { content: [{ type: 'text', text: JSON.stringify({ fields: cached, source: 'cache' }, null, 2) }], }; } const params: Record<string, string | number | boolean | undefined> = { page: 1, page_size: 1, project_id, }; const result = await client.get<MantisPaginatedIssues>('issues', params); const issues = result.issues ?? []; let fields: string[]; if (issues.length === 0) { fields = STATIC_ISSUE_FIELDS; } else { const discovered = Object.keys(issues[0]); fields = Array.from(new Set([...discovered, ...EMPTY_STRIPPED_FIELDS])).sort(); } await cache.saveIssueFields(fields); return { content: [{ type: 'text', text: JSON.stringify({ fields, source: issues.length > 0 ? 'live' : 'static' }, null, 2) }], }; } catch (error) { const msg = error instanceof Error ? error.message : String(error); return { content: [{ type: 'text', text: errorText(msg) }], isError: true }; } } );