UNPKG

@the_cfdude/productboard-mcp

Version:

Model Context Protocol server for Productboard REST API with dynamic tool loading

407 lines (406 loc) 14.9 kB
/** * Custom fields management tools for hierarchy entities */ import { withContext, formatResponse } from '../utils/tool-wrapper.js'; import { isEnterpriseError } from '../utils/parameter-utils.js'; import { ProductboardError } from '../errors/index.js'; import { ErrorCode } from '@modelcontextprotocol/sdk/types.js'; export function setupCustomFieldsTools() { return [ { name: 'get_custom_fields', description: 'List all custom fields for hierarchy entities', inputSchema: { type: 'object', properties: { type: { type: 'array', description: 'Array of custom field types to filter by', items: { type: 'string', enum: [ 'text', 'custom-description', 'number', 'dropdown', 'multi-dropdown', 'member', ], }, }, instance: { type: 'string', description: 'Productboard instance name (optional)', }, workspaceId: { type: 'string', description: 'Workspace ID (optional)', }, }, required: ['type'], }, }, { name: 'get_custom_field', description: 'Retrieve a specific custom field', inputSchema: { type: 'object', properties: { id: { type: 'string', description: 'Custom field ID', }, instance: { type: 'string', description: 'Productboard instance name (optional)', }, workspaceId: { type: 'string', description: 'Workspace ID (optional)', }, }, required: ['id'], }, }, { name: 'get_custom_fields_values', description: 'List all custom field values', inputSchema: { type: 'object', properties: { type: { type: 'array', description: 'Array of custom field types (mandatory if customField.id not specified)', items: { type: 'string', enum: [ 'text', 'custom-description', 'number', 'dropdown', 'multi-dropdown', 'member', ], }, }, 'customField.id': { type: 'string', description: 'Show values for specific custom field (mandatory if type not specified)', }, 'hierarchyEntity.id': { type: 'string', description: 'Show values for specific hierarchy entity (optional)', }, instance: { type: 'string', description: 'Productboard instance name (optional)', }, workspaceId: { type: 'string', description: 'Workspace ID (optional)', }, }, }, }, { name: 'get_custom_field_value', description: 'Retrieve a custom field value for a hierarchy entity', inputSchema: { type: 'object', properties: { 'customField.id': { type: 'string', description: 'ID of the custom field', }, 'hierarchyEntity.id': { type: 'string', description: 'ID of the hierarchy entity', }, instance: { type: 'string', description: 'Productboard instance name (optional)', }, workspaceId: { type: 'string', description: 'Workspace ID (optional)', }, }, required: ['customField.id', 'hierarchyEntity.id'], }, }, { name: 'set_custom_field_value', description: 'Set value of a custom field for a hierarchy entity', inputSchema: { type: 'object', properties: { 'customField.id': { type: 'string', description: 'ID of the custom field to be set', }, 'hierarchyEntity.id': { type: 'string', description: 'ID of the hierarchy entity', }, body: { type: 'object', description: 'Custom field value data', properties: { type: { type: 'string', description: 'Type of custom field', enum: [ 'text', 'custom-description', 'number', 'dropdown', 'multi-dropdown', 'member', ], }, value: { description: 'Field value content (string for text fields, max 1024 chars)', anyOf: [ { type: 'string', maxLength: 1024 }, { type: 'number' }, { type: 'array' }, ], }, }, required: ['type', 'value'], }, instance: { type: 'string', description: 'Productboard instance name (optional)', }, workspaceId: { type: 'string', description: 'Workspace ID (optional)', }, }, required: ['customField.id', 'hierarchyEntity.id', 'body'], }, }, { name: 'delete_custom_field_value', description: 'Delete value of a custom field for a hierarchy entity', inputSchema: { type: 'object', properties: { 'customField.id': { type: 'string', description: 'ID of the custom field', }, 'hierarchyEntity.id': { type: 'string', description: 'ID of the hierarchy entity', }, instance: { type: 'string', description: 'Productboard instance name (optional)', }, workspaceId: { type: 'string', description: 'Workspace ID (optional)', }, }, required: ['customField.id', 'hierarchyEntity.id'], }, }, { name: 'get_feature_statuses', description: 'List all feature statuses', inputSchema: { type: 'object', properties: { instance: { type: 'string', description: 'Productboard instance name (optional)', }, workspaceId: { type: 'string', description: 'Workspace ID (optional)', }, }, }, }, ]; } export async function handleCustomFieldsTool(name, args) { try { switch (name) { case 'get_custom_fields': return await getCustomFields(args); case 'get_custom_field': return await getCustomField(args); case 'get_custom_fields_values': return await getCustomFieldsValues(args); case 'get_custom_field_value': return await getCustomFieldValue(args); case 'set_custom_field_value': return await setCustomFieldValue(args); case 'delete_custom_field_value': return await deleteCustomFieldValue(args); case 'get_feature_statuses': return await getFeatureStatuses(args); default: throw new Error(`Unknown custom fields tool: ${name}`); } } catch (error) { const enterpriseInfo = isEnterpriseError(error); if (enterpriseInfo.isEnterpriseFeature) { throw new ProductboardError(ErrorCode.InvalidRequest, enterpriseInfo.message, error); } throw error; } } async function getCustomFields(args) { return await withContext(async (context) => { // Debug logging disabled for production const params = {}; // Add the type parameter if provided - API expects array format if (args.type && Array.isArray(args.type)) { params.type = args.type; } // Parameters prepared const response = await context.axios.get('/hierarchy-entities/custom-fields', { params, paramsSerializer: { indexes: null, // This tells axios to repeat array parameters instead of using indices }, }); return { content: [ { type: 'text', text: formatResponse(response.data), }, ], }; }, args.instance, args.workspaceId); } async function getCustomField(args) { return await withContext(async (context) => { const response = await context.axios.get(`/hierarchy-entities/custom-fields/${args.id}`); return { content: [ { type: 'text', text: formatResponse(response.data), }, ], }; }, args.instance, args.workspaceId); } async function getCustomFieldsValues(args) { return await withContext(async (context) => { const params = {}; // Add optional parameters if (args.type && Array.isArray(args.type)) { params.type = args.type; } if (args['customField.id']) { params['customField.id'] = args['customField.id']; } if (args['hierarchyEntity.id']) { params['hierarchyEntity.id'] = args['hierarchyEntity.id']; } const response = await context.axios.get('/hierarchy-entities/custom-fields-values', { params, }); return { content: [ { type: 'text', text: formatResponse(response.data), }, ], }; }, args.instance, args.workspaceId); } async function getCustomFieldValue(args) { return await withContext(async (context) => { const params = { 'customField.id': args['customField.id'], 'hierarchyEntity.id': args['hierarchyEntity.id'], }; const response = await context.axios.get('/hierarchy-entities/custom-fields-values/value', { params, }); return { content: [ { type: 'text', text: formatResponse(response.data), }, ], }; }, args.instance, args.workspaceId); } async function setCustomFieldValue(args) { return await withContext(async (context) => { // Handle case where body might be passed as a JSON string let body = args.body; if (typeof body === 'string') { try { body = JSON.parse(body); } catch { throw new Error('Invalid JSON in body parameter'); } } const params = { 'customField.id': args['customField.id'], 'hierarchyEntity.id': args['hierarchyEntity.id'], }; const response = await context.axios.put('/hierarchy-entities/custom-fields-values/value', { data: body, }, { params, }); return { content: [ { type: 'text', text: formatResponse(response.data), }, ], }; }, args.instance, args.workspaceId); } async function deleteCustomFieldValue(args) { return await withContext(async (context) => { const params = { 'customField.id': args['customField.id'], 'hierarchyEntity.id': args['hierarchyEntity.id'], }; await context.axios.delete('/hierarchy-entities/custom-fields-values/value', { params, }); return { content: [ { type: 'text', text: JSON.stringify({ success: true, message: `Custom field value deleted successfully`, }), }, ], }; }, args.instance, args.workspaceId); } async function getFeatureStatuses(args) { return await withContext(async (context) => { const response = await context.axios.get('/feature-statuses'); return { content: [ { type: 'text', text: formatResponse(response.data), }, ], }; }, args.instance, args.workspaceId); }