Skip to main content
Glama

add_relationship

Link two MantisBT issues with relationships like duplicate_of, related_to, depends_on, or blocks to track dependencies and connections between bugs.

Instructions

Add a relationship between two MantisBT issues.

Relationship types — use either type_id (numeric) or type_name (string):

  • 0 / "duplicate_of" — this issue is a duplicate of target

  • 1 / "related_to" — this issue is related to target

  • 2 / "parent_of" — this issue depends on target (target must be done first); alias: "depends_on"

  • 3 / "child_of" — this issue blocks target (target can't proceed until this is done); alias: "blocks"

  • 4 / "has_duplicate" — this issue has target as a duplicate

Directionality note: "A child_of B" means A blocks B. "A parent_of B" means A depends on B.

Dash variants (e.g. "related-to") are also accepted for type_name.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
issue_idYesThe source issue ID (the one the relationship is added to)
target_idYesThe target issue ID
type_idNoRelationship type ID: 0=duplicate_of, 1=related_to, 2=parent_of (depends on), 3=child_of (blocks), 4=has_duplicate. Use either type_id or type_name.
type_nameNoRelationship type name as alternative to type_id. Accepted: "duplicate_of", "related_to", "parent_of" (or "depends_on"), "child_of" (or "blocks"), "has_duplicate". Dash variants (e.g. "related-to") also work.

Implementation Reference

  • The tool 'add_relationship' is registered using server.registerTool, defining its title, description, and input schema.
      server.registerTool(
        'add_relationship',
        {
          title: 'Add Issue Relationship',
          description: `Add a relationship between two MantisBT issues.
    
    Relationship types — use either type_id (numeric) or type_name (string):
    - ${RELATIONSHIP_TYPES.DUPLICATE_OF} / "duplicate_of"  — this issue is a duplicate of target
    - ${RELATIONSHIP_TYPES.RELATED_TO}   / "related_to"    — this issue is related to target
    - ${RELATIONSHIP_TYPES.PARENT_OF}    / "parent_of"     — this issue depends on target (target must be done first); alias: "depends_on"
    - ${RELATIONSHIP_TYPES.CHILD_OF}     / "child_of"      — this issue blocks target (target can't proceed until this is done); alias: "blocks"
    - ${RELATIONSHIP_TYPES.HAS_DUPLICATE} / "has_duplicate" — this issue has target as a duplicate
    
    Directionality note: "A child_of B" means A blocks B. "A parent_of B" means A depends on B.
    
    Dash variants (e.g. "related-to") are also accepted for type_name.`,
          inputSchema: z.object({
            issue_id: z.coerce.number().int().positive().describe('The source issue ID (the one the relationship is added to)'),
            target_id: z.coerce.number().int().positive().describe('The target issue ID'),
            type_id: z.coerce.number().int().min(0).max(4).optional().describe(
              'Relationship type ID: 0=duplicate_of, 1=related_to, 2=parent_of (depends on), 3=child_of (blocks), 4=has_duplicate. Use either type_id or type_name.'
            ),
            type_name: z.string().optional().describe(
              'Relationship type name as alternative to type_id. Accepted: "duplicate_of", "related_to", "parent_of" (or "depends_on"), "child_of" (or "blocks"), "has_duplicate". Dash variants (e.g. "related-to") also work.'
            ),
          }),
          annotations: {
            readOnlyHint: false,
            destructiveHint: false,
            idempotentHint: false,
          },
        },
        async ({ issue_id, target_id, type_id, type_name }) => {
          // Resolve type_id from type_name when type_id is not provided
          let resolvedTypeId = type_id;
          if (resolvedTypeId === undefined) {
            if (type_name === undefined) {
              return { content: [{ type: 'text', text: errorText('Either type_id or type_name must be provided') }], isError: true };
            }
            const normalized = type_name.toLowerCase().replace(/-/g, '_');
            resolvedTypeId = RELATIONSHIP_NAME_TO_ID[normalized];
            if (resolvedTypeId === undefined) {
              return {
                content: [{ type: 'text', text: errorText(`Unknown relationship type name: "${type_name}". Valid values: duplicate_of, related_to, parent_of, child_of, has_duplicate`) }],
                isError: true,
              };
            }
          }
    
          try {
            const body = {
              issue: { id: target_id },
              type: { id: resolvedTypeId },
            };
            const result = await client.post<unknown>(`issues/${issue_id}/relationships`, body);
            return {
              content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
            };
          } catch (error) {
            const msg = error instanceof Error ? error.message : String(error);
            return { content: [{ type: 'text', text: errorText(msg) }], isError: true };
          }
        }
      );
  • The tool's implementation function, which resolves the relationship type ID and makes a POST request to the MantisBT API to create the relationship.
    async ({ issue_id, target_id, type_id, type_name }) => {
      // Resolve type_id from type_name when type_id is not provided
      let resolvedTypeId = type_id;
      if (resolvedTypeId === undefined) {
        if (type_name === undefined) {
          return { content: [{ type: 'text', text: errorText('Either type_id or type_name must be provided') }], isError: true };
        }
        const normalized = type_name.toLowerCase().replace(/-/g, '_');
        resolvedTypeId = RELATIONSHIP_NAME_TO_ID[normalized];
        if (resolvedTypeId === undefined) {
          return {
            content: [{ type: 'text', text: errorText(`Unknown relationship type name: "${type_name}". Valid values: duplicate_of, related_to, parent_of, child_of, has_duplicate`) }],
            isError: true,
          };
        }
      }
    
      try {
        const body = {
          issue: { id: target_id },
          type: { id: resolvedTypeId },
        };
        const result = await client.post<unknown>(`issues/${issue_id}/relationships`, body);
        return {
          content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
        };
      } catch (error) {
        const msg = error instanceof Error ? error.message : String(error);
        return { content: [{ type: 'text', text: errorText(msg) }], isError: true };
      }
    }
  • The input schema for 'add_relationship', validating issue_id, target_id, type_id, and type_name.
    inputSchema: z.object({
      issue_id: z.coerce.number().int().positive().describe('The source issue ID (the one the relationship is added to)'),
      target_id: z.coerce.number().int().positive().describe('The target issue ID'),
      type_id: z.coerce.number().int().min(0).max(4).optional().describe(
        'Relationship type ID: 0=duplicate_of, 1=related_to, 2=parent_of (depends on), 3=child_of (blocks), 4=has_duplicate. Use either type_id or type_name.'
      ),
      type_name: z.string().optional().describe(
        'Relationship type name as alternative to type_id. Accepted: "duplicate_of", "related_to", "parent_of" (or "depends_on"), "child_of" (or "blocks"), "has_duplicate". Dash variants (e.g. "related-to") also work.'
      ),
    }),

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