Skip to main content
Glama
ttpears

GitLab MCP Server

by ttpears

Get Issue Context

get_issue_context
Read-onlyIdempotent

Retrieve all context for an issue in a single call: body, notes, related and closing merge requests, and linked issues.

Instructions

Bundle issue body, all notes (paginated up to maxNotes), related merge requests (mentioning), closing merge requests, linked issues (relates_to/blocks/is_blocked_by) into a single call. Use this instead of fanning out across get_issues + get_notes + search_merge_requests when investigating an issue.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
projectPathYesFull project path (e.g. "my-group/my-project")
iidYesIssue IID (the number shown in the GitLab UI)
maxNotesNoCap on notes fetched. Default 100.
includeSystemNotesNoInclude system-generated notes (label changes, assignment events). Default false.
userCredentialsNoYour GitLab credentials (optional — falls back to the configured env token if not provided)

Implementation Reference

  • src/tools.ts:2282-2284 (registration)
    The 'get_issue_context' tool is registered in the readOnlyTools array, which is then spread into the exports.tools array.
    getNotesTool,
    getIssueContextTool,
    getMergeRequestContextTool,
  • Input schema for get_issue_context: projectPath (required), iid (required), maxNotes (default 100, cap 500), includeSystemNotes (default false).
    inputSchema: withUserAuth(z.object({
      projectPath: z.string().min(1).describe('Full project path (e.g. "my-group/my-project")'),
      iid: z.string().min(1).describe('Issue IID (the number shown in the GitLab UI)'),
      maxNotes: z.number().int().min(1).max(500).default(100).describe('Cap on notes fetched. Default 100.'),
      includeSystemNotes: z.boolean().default(false).describe('Include system-generated notes (label changes, assignment events). Default false.'),
    })),
  • Handler function for get_issue_context. Fetches issue detail, notes, related MRs, closing MRs, and linked issues in parallel via Promise.all, then assembles a bundled response.
      handler: async (input, client, userConfig) => {
        const credentials = input.userCredentials ? validateUserConfig(input.userCredentials) : userConfig;
        const projectPath = input.projectPath.trim();
        const iid = input.iid.trim();
    
        const [detail, notesResult, related, closedBy, links] = await Promise.all([
          client.getIssueDetail(projectPath, iid, credentials),
          client.getNotes(projectPath, 'issue', iid, input.maxNotes, undefined, true, credentials),
          client.getIssueRelatedMergeRequests(projectPath, iid, credentials).catch(() => []),
          client.getIssueClosedBy(projectPath, iid, credentials).catch(() => []),
          client.getIssueLinks(projectPath, iid, credentials).catch(() => []),
        ]);
    
        const issue = detail?.project?.issue;
        if (!issue) {
          throw new Error(`Issue ${projectPath}#${iid} not found.`);
        }
    
        const allNotes: any[] = Array.isArray(notesResult?.nodes) ? notesResult.nodes : [];
        const notes = input.includeSystemNotes ? allNotes : allNotes.filter((n) => !n.system);
    
        const summarizeMR = (mr: any) => ({
          iid: mr.iid,
          title: mr.title,
          state: mr.state,
          webUrl: mr.web_url ?? mr.webUrl,
          author: mr.author?.username ?? null,
          sourceBranch: mr.source_branch ?? mr.sourceBranch,
          targetBranch: mr.target_branch ?? mr.targetBranch,
        });
    
        const summarizeLink = (l: any) => ({
          iid: String(l.iid),
          title: l.title,
          state: l.state,
          linkType: l.link_type ?? null,
          webUrl: l.web_url ?? null,
          projectId: l.project_id ?? null,
        });
    
        return {
          project: { fullPath: projectPath },
          issue,
          notes: {
            count: notes.length,
            truncated: !!notesResult?.hasMore,
            nodes: notes,
          },
          relatedMergeRequests: related.map(summarizeMR),
          closingMergeRequests: closedBy.map(summarizeMR),
          linkedIssues: links.map(summarizeLink),
        };
      },
    };
  • getIssueDetail: GraphQL query to fetch issue details (title, description, state, author, assignees, labels, milestone, votes, etc.)
    async getIssueDetail(
      projectPath: string,
      iid: string,
      userConfig?: UserConfig,
    ): Promise<any> {
      const query = gql`
        query getIssueDetail($projectPath: ID!, $iid: String!) {
          project(fullPath: $projectPath) {
            fullPath
            issue(iid: $iid) {
              id
              iid
              title
              description
              state
              confidential
              createdAt
              updatedAt
              closedAt
              webUrl
              author { username name }
              assignees { nodes { username name } }
              labels { nodes { title color } }
              milestone { title state dueDate webPath }
              userNotesCount
              upvotes
              downvotes
            }
          }
        }
      `;
      return this.query(query, { projectPath, iid }, userConfig);
    }
  • getIssueRelatedMergeRequests, getIssueClosedBy, getIssueLinks: REST API helpers called by the handler to fetch related MRs, closing MRs, and linked issues.
    async getIssueRelatedMergeRequests(
      projectPath: string,
      iid: string,
      userConfig?: UserConfig,
    ): Promise<any[]> {
      const encodedPath = encodeURIComponent(projectPath);
      return this.restRequest('GET', `/projects/${encodedPath}/issues/${iid}/related_merge_requests`, {
        userConfig,
      });
    }
Behavior4/5

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

Annotations already indicate read-only and idempotent behavior. The description adds that notes are paginated up to maxNotes, which is useful. It doesn't discuss limitations like potential incompleteness if many notes exist, but overall it adds value beyond annotations.

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?

The description is two sentences: the first lists what the tool does, the second gives usage guidance. No wasted words, highly efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (bundling multiple data types) and lack of output schema, the description sufficiently describes what is returned (issue body, notes, merge requests, linked issues). It could mention the return format, but the components listed are clear enough for an agent.

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?

With 100% schema description coverage, the schema already documents parameters. The description reinforces the purpose of maxNotes (paginated) and includeSystemNotes (system-generated), but does not add significant new meaning beyond the schema.

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 tool bundles multiple pieces of issue-related data (body, notes, merge requests, linked issues) into one call. It distinguishes itself from sibling tools by explicitly naming the alternatives it replaces.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states 'Use this instead of fanning out across get_issues + get_notes + search_merge_requests when investigating an issue,' providing clear when-to-use and what-to-use-instead guidance.

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/ttpears/gitlab-mcp'

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