Skip to main content
Glama
mrchris2000

MCP DevOps Plan Server

by mrchris2000

change_work_item_state

Transition work items between states like Resolve, Close, or Reopen in DevOps Plan systems using a two-step movement and commit process.

Instructions

Changes the state of a work item in Plan using a two-step process (movement request + commit)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
dbidYesThe dbid field from the workitem to identify it, this is the first field returned for each workitem in the get_work_items tool, or from the create_work_item tool as the dbId field.
applicationYesName of the application
targetStateYesThe target state to transition the work item to (e.g., 'Resolve', 'Close', 'Reopen', etc.)

Implementation Reference

  • Implementation of the `change_work_item_state` MCP tool, which performs a two-step state transition (movement request followed by commit) using the CCM REST API.
    server.tool(
        "change_work_item_state",
        "Changes the state of a work item in Plan using a two-step process (movement request + commit)",
        {
            dbid: z.string().describe("The dbid field from the workitem to identify it, this is the first field returned for each workitem in the get_work_items tool."),
            application: z.string().describe("Name of the application"),
            targetState: z.string().describe("The target state to transition the work item to (e.g., 'Resolve', 'Close', 'Reopen', etc.)")
        },
        async ({ dbid, application, targetState }) => {
            try {
                if (!globalCookies) {
                    globalCookies = await getCookiesFromServer(serverURL);
                    if (!globalCookies) {
                        console.error("Failed to retrieve cookies from server.");
                        return { error: "Failed to retrieve cookies." };
                    }
                    console.log("Received Cookies:", globalCookies);
                } else {
                    console.log("Reusing Stored Cookies:", globalCookies);
                }
    
                // First, get the current work item data
                const getCurrentUrl = `${serverURL}/ccmweb/rest/repos/${teamspaceID}/databases/${application}/records/WorkItem/${dbid}?useDbid=true`;
                
                const getCurrentResponse = await fetch(getCurrentUrl, {
                    method: 'GET',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': `Basic ${personal_access_token_string}`,
                        'Cookie': globalCookies
                    }
                });
    
                if (!getCurrentResponse.ok) {
                    const errorText = await getCurrentResponse.text();
                    throw new Error(`Failed to get current work item data with status ${getCurrentResponse.status}: ${errorText}`);
                }
    
                const currentWorkItem = await getCurrentResponse.json();
                
                // Step 1: Make the movement request with minimal body
                const movementUrl = `${serverURL}/ccmweb/rest/repos/${teamspaceID}/databases/${application}/records/WorkItem/${dbid}?actionName=${targetState}&operation=Edit&useDbid=true`;
                
                const movementResponse = await fetch(movementUrl, {
                    method: 'PATCH',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': `Basic ${personal_access_token_string}`,
                        'Cookie': globalCookies
                    },
                    body: "{}"  // Minimal body like in browser
                });
    
                if (!movementResponse.ok) {
                    const errorText = await movementResponse.text();
                    throw new Error(`Movement request failed with status ${movementResponse.status}: ${errorText}`);
                }
    
                const movementData = await movementResponse.json();
                console.log("Movement request successful:", movementData);
    
                // Wait 1 second before commit to allow database updates to complete
                await new Promise(resolve => setTimeout(resolve, 1000));
    
                // Step 2: Commit the change with minimal body (like browser)
                const commitUrl = `${serverURL}/ccmweb/rest/repos/${teamspaceID}/databases/${application}/records/WorkItem/${dbid}?operation=Commit&useDbid=true`;
                
                // Use the same minimal commit body structure as the browser
                const commitBody = {
                    "dbId": movementData.dbId,
                    "fields": []
                };
                
                const commitResponse = await fetch(commitUrl, {
                    method: 'PATCH',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': `Basic ${personal_access_token_string}`,
                        'Cookie': globalCookies
                    },
                    body: JSON.stringify(commitBody)
                });
    
                if (!commitResponse.ok) {
                    const errorText = await commitResponse.text();
                    throw new Error(`Commit request failed with status ${commitResponse.status}: ${errorText}`);
                }
    
                const commitData = await commitResponse.json();
                console.log("Commit request successful:", commitData);
    
                return {
                    content: [{ 
                        type: 'text', 
                        text: `Work item ${dbid} state successfully changed to '${targetState}'. Both movement and commit operations completed successfully.` 
                    }]
                };
    
            } catch (e) {
                // Handle specific state transition errors
                if (e.message.includes('status 400') || e.message.includes('status 422')) {
                    return {
                        content: [{ 
                            type: 'text', 
                            text: `State transition error: The transition from current state to '${targetState}' may not be valid for work item ${dbid}. Error: ${e.message}` 
                        }]
                    };
                } else {
                    return {
                        content: [{ 
                            type: 'text', 
                            text: `Error changing work item state: ${e.message}` 
                        }]
                    };
                }
            }
        }
    );

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/mrchris2000/mcp-devops-plan'

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