Skip to main content
Glama

Update Issue Field

update_issue_field

Modify custom fields on Jira issues to track progress, decisions, risks, health status, and completion percentages for project management.

Instructions

Update a custom field on a Jira issue. Supported fields: Decision Needed, Progress Update, Decision Maker(s), Risks/Blockers, Completion Percentage, Health Status. You can use either the field name or field ID.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
issueKeyYesThe issue key (e.g., "TSSE-984")
fieldNameOrIdYesField name or ID. Valid names: Decision Needed, Progress Update, Decision Maker(s), Risks/Blockers, Completion Percentage, Health Status
valueYesThe value to set. For rich text fields, provide plain text. For select fields, provide the option value. For user fields, provide accountId. For number fields, provide a number.

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
errorNo
fieldIdNo
successYes
fieldNameNo

Implementation Reference

  • Core handler function that resolves the field ID, validates and formats the value based on field type (richtext, number, select, user), makes the Jira PUT API request to update the issue field, and returns success with field details.
    export async function updateIssueField(
      issueKey: string,
      fieldNameOrId: string,
      value: unknown
    ): Promise<{ success: boolean; fieldId: string; fieldName: string }> {
      const fieldId = resolveFieldId(fieldNameOrId);
      const fieldName = CUSTOM_FIELD_MAP[fieldId] || fieldId;
      const fieldType = CUSTOM_FIELD_TYPES[fieldId];
    
      let formattedValue: unknown;
    
      switch (fieldType) {
        case 'richtext':
          // If value is already an ADF object, use it; otherwise create one from text
          if (typeof value === 'object' && value !== null && 'type' in value) {
            formattedValue = value;
          } else {
            formattedValue = createADFDocument(String(value));
          }
          break;
        case 'number':
          formattedValue = typeof value === 'number' ? value : parseFloat(String(value));
          if (isNaN(formattedValue as number)) {
            throw new Error(`Invalid number value for ${fieldName}: ${value}`);
          }
          // Special handling for Completion Percentage: convert percentage (0-100) to decimal (0.0-1.0)
          if (fieldId === 'customfield_15116') {
            formattedValue = (formattedValue as number) / 100;
          }
          break;
        case 'select':
          // Select fields need to be set with { value: "option" } format
          formattedValue = typeof value === 'object' ? value : { value: String(value) };
          break;
        case 'user':
          // User picker fields need accountId
          formattedValue = typeof value === 'object' ? value : { accountId: String(value) };
          break;
        default:
          formattedValue = value;
      }
    
      await jiraFetch<void>(`/issue/${issueKey}`, {
        method: 'PUT',
        body: JSON.stringify({
          fields: {
            [fieldId]: formattedValue,
          },
        }),
      });
    
      return { success: true, fieldId, fieldName };
    }
  • src/index.ts:467-514 (registration)
    MCP server tool registration for 'update_issue_field', including title, description, input/output schemas using Zod, and a thin wrapper async handler that performs basic validation and delegates to the core updateIssueField function.
    server.registerTool(
      'update_issue_field',
      {
        title: 'Update Issue Field',
        description: `Update a custom field on a Jira issue. Supported fields: ${Object.values(CUSTOM_FIELD_MAP).join(', ')}. You can use either the field name or field ID.`,
        inputSchema: {
          issueKey: z.string().describe('The issue key (e.g., "TSSE-984")'),
          fieldNameOrId: z.string().describe(`Field name or ID. Valid names: ${Object.values(CUSTOM_FIELD_MAP).join(', ')}`),
          value: z.union([z.string(), z.number(), z.object({})]).describe('The value to set. For rich text fields, provide plain text. For select fields, provide the option value. For user fields, provide accountId. For number fields, provide a number.'),
        },
        outputSchema: {
          success: z.boolean(),
          fieldId: z.string().optional(),
          fieldName: z.string().optional(),
          error: z.object({
            message: z.string(),
            statusCode: z.number().optional(),
            details: z.unknown().optional(),
          }).optional(),
        },
      },
      async ({ issueKey, fieldNameOrId, value }) => {
        try {
          if (!issueKey || !issueKey.trim()) {
            throw new Error('issueKey is required');
          }
          if (!fieldNameOrId || !fieldNameOrId.trim()) {
            throw new Error('fieldNameOrId is required');
          }
          if (value === undefined || value === null) {
            throw new Error('value is required');
          }
    
          const result = await updateIssueField(issueKey, fieldNameOrId, value);
          return {
            content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
            structuredContent: result,
          };
        } catch (error) {
          const output = { success: false, ...formatError(error) };
          return {
            content: [{ type: 'text', text: JSON.stringify(output, null, 2) }],
            structuredContent: output,
            isError: true,
          };
        }
      }
    );
  • Zod-based input and output schemas defining the tool interface: inputs are issueKey (string), fieldNameOrId (string), value (string|number|object); output includes success boolean, fieldId, fieldName, and optional error.
    {
      title: 'Update Issue Field',
      description: `Update a custom field on a Jira issue. Supported fields: ${Object.values(CUSTOM_FIELD_MAP).join(', ')}. You can use either the field name or field ID.`,
      inputSchema: {
        issueKey: z.string().describe('The issue key (e.g., "TSSE-984")'),
        fieldNameOrId: z.string().describe(`Field name or ID. Valid names: ${Object.values(CUSTOM_FIELD_MAP).join(', ')}`),
        value: z.union([z.string(), z.number(), z.object({})]).describe('The value to set. For rich text fields, provide plain text. For select fields, provide the option value. For user fields, provide accountId. For number fields, provide a number.'),
      },
      outputSchema: {
        success: z.boolean(),
        fieldId: z.string().optional(),
        fieldName: z.string().optional(),
        error: z.object({
          message: z.string(),
          statusCode: z.number().optional(),
          details: z.unknown().optional(),
        }).optional(),
      },
    },
  • Helper function used by the handler to map human-readable field names (e.g., 'Health Status') to Jira customfield IDs using FIELD_NAME_TO_ID reverse mapping, or pass through if already an ID.
    export function resolveFieldId(fieldNameOrId: string): string {
      // If it's already a customfield ID, return as-is
      if (fieldNameOrId.startsWith('customfield_')) {
        return fieldNameOrId;
      }
    
      // Try to find by name (case-insensitive)
      const fieldId = FIELD_NAME_TO_ID[fieldNameOrId.toLowerCase()];
      if (fieldId) {
        return fieldId;
      }
    
      throw new Error(`Unknown field: "${fieldNameOrId}". Valid fields are: ${Object.values(CUSTOM_FIELD_MAP).join(', ')}`);
    }
  • Type definitions for custom fields used in value formatting: specifies 'richtext', 'user', 'number', 'select' for proper ADF conversion, decimal scaling, etc.
    export const CUSTOM_FIELD_TYPES: Record<string, CustomFieldType> = {
      'customfield_15111': 'richtext',  // Decision Needed
      'customfield_15112': 'richtext',  // Progress Update
      'customfield_15113': 'user',      // Decision Maker(s)
      'customfield_15115': 'richtext',  // Risks/Blockers
      'customfield_15116': 'number',    // Completion Percentage
      'customfield_15117': 'select',    // Health Status
    };
Behavior2/5

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

With no annotations provided, the description carries full burden but lacks critical behavioral details. It doesn't disclose permission requirements, whether this is a destructive mutation, rate limits, error handling, or what happens to existing field values. The description only covers basic functionality without safety or operational context.

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?

Two sentences with zero waste. The first sentence states the core purpose, the second provides essential usage detail about field identification. Every word serves a clear purpose and the structure is front-loaded with the most important information.

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 this is a mutation tool with no annotations and an output schema exists, the description is minimally adequate but incomplete. It covers what the tool does but lacks behavioral context about permissions, side effects, or error conditions that would be crucial for safe agent usage.

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?

Schema description coverage is 100%, so the schema already documents all three parameters thoroughly. The description adds minimal value by listing supported field names, which partially overlaps with the schema's 'fieldNameOrId' description. No additional parameter semantics beyond what's in the schema are provided.

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

Purpose5/5

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

The description clearly states the specific action ('Update a custom field on a Jira issue') and distinguishes it from siblings like 'update_progress' or 'add_comment' by specifying it's for custom fields only. It lists the exact supported fields, making the purpose unambiguous.

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 implies usage by listing supported fields, but doesn't explicitly state when to use this tool versus alternatives like 'update_progress' or general issue updates. No guidance on prerequisites, exclusions, or specific scenarios is provided.

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/eh24905-wiz/jira-mcp'

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