Skip to main content
Glama
mmntm

Weblate MCP Server

by mmntm

getProjectChanges

Retrieve recent modifications for a Weblate translation project to track updates and monitor translation activity.

Instructions

Get recent changes for a specific project

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
projectSlugYesThe slug of the project

Implementation Reference

  • Primary handler for the 'getProjectChanges' MCP tool. Includes registration via @Tool decorator, input schema validation with Zod, execution logic that calls the Weblate API service, formats results using helper methods, handles empty results and errors, and returns structured content.
    @Tool({
      name: 'getProjectChanges',
      description: 'Get recent changes for a specific project',
      parameters: z.object({
        projectSlug: z.string().describe('The slug of the project'),
      }),
    })
    async getProjectChanges({ projectSlug }: { projectSlug: string }) {
      try {
        const result = await this.weblateApiService.getProjectChanges(projectSlug);
    
        if (result.results.length === 0) {
          return {
            content: [
              {
                type: 'text',
                text: `No changes found for project "${projectSlug}".`,
              },
            ],
          };
        }
    
        const changesList = result.results
          .slice(0, 20)
          .map(change => this.formatChangeResult(change))
          .join('\n\n---\n\n');
    
        return {
          content: [
            {
              type: 'text',
              text: `Recent changes in project "${projectSlug}" (${result.count} total):\n\n${changesList}`,
            },
          ],
        };
      } catch (error) {
        this.logger.error(`Failed to get changes for project ${projectSlug}`, error);
        return {
          content: [
            {
              type: 'text',
              text: `Error getting changes for project "${projectSlug}": ${error.message}`,
            },
          ],
          isError: true,
        };
      }
    }
  • Core service method that performs the actual API call to retrieve project changes using projectsChangesRetrieve from the Weblate client, handles response parsing, and error management. Called by the tool handler via proxies.
    async getProjectChanges(
      projectSlug: string
    ): Promise<{ results: Change[]; count: number; next?: string; previous?: string }> {
      try {
        const client = this.weblateClientService.getClient();
        
        const response = await projectsChangesRetrieve({
          client,
          path: { slug: projectSlug },
        });
        
        const changeList = response.data as any;
        
        // Handle different response formats
        if (Array.isArray(changeList)) {
          return {
            results: changeList,
            count: changeList.length,
          };
        }
        
        if (changeList && changeList.results) {
          return {
            results: changeList.results || [],
            count: changeList.count || 0,
            next: changeList.next || undefined,
            previous: changeList.previous || undefined,
          };
        }
        
        return { results: [], count: 0 };
      } catch (error) {
        this.logger.error(`Failed to get changes for project ${projectSlug}`, error);
        throw new Error(`Failed to get project changes: ${error.message}`);
      }
    }
  • Helper method used by the handler to format each change into a markdown string with action, user, time, and target details.
    }
    
    @Tool({
      name: 'getComponentChanges',
      description: 'Get recent changes for a specific component',
      parameters: z.object({
        projectSlug: z.string().describe('The slug of the project'),
        componentSlug: z.string().describe('The slug of the component'),
      }),
    })
    async getComponentChanges({
      projectSlug,
      componentSlug,
    }: {
      projectSlug: string;
      componentSlug: string;
    }) {
      try {
        const result = await this.weblateApiService.getComponentChanges(
          projectSlug,
          componentSlug,
        );
    
        if (result.results.length === 0) {
          return {
            content: [
              {
                type: 'text',
                text: `No changes found for component "${componentSlug}" in project "${projectSlug}".`,
              },
            ],
          };
        }
    
        const changesList = result.results
          .slice(0, 20)
          .map(change => this.formatChangeResult(change))
          .join('\n\n---\n\n');
    
        return {
          content: [
            {
              type: 'text',
              text: `Recent changes in component "${componentSlug}" (${result.count} total):\n\n${changesList}`,
            },
          ],
        };
      } catch (error) {
        this.logger.error(
          `Failed to get changes for component ${componentSlug} in project ${projectSlug}`,
          error,
        );
        return {
          content: [
            {
              type: 'text',
              text: `Error getting changes for component "${componentSlug}": ${error.message}`,
            },
          ],
          isError: true,
        };
      }
    }
    
    @Tool({
      name: 'getChangesByUser',
      description: 'Get recent changes by a specific user',
      parameters: z.object({
        user: z.string().describe('Username to filter by'),
        limit: z.number().optional().describe('Number of changes to return (default: 20)').default(20),
      }),
    })
    async getChangesByUser({
      user,
      limit = 20,
    }: {
      user: string;
      limit?: number;
    }) {
      try {
        const result = await this.weblateApiService.getChangesByUser(user, limit);
    
        if (result.results.length === 0) {
          return {
            content: [
              {
                type: 'text',
                text: `No changes found for user "${user}".`,
              },
            ],
          };
        }
    
        const changesList = result.results
          .slice(0, limit)
          .map(change => this.formatChangeResult(change))
          .join('\n\n---\n\n');
    
        return {
          content: [
            {
              type: 'text',
              text: `Recent changes by user "${user}" (${result.count} total):\n\n${changesList}`,
            },
          ],
        };
      } catch (error) {
        this.logger.error(`Failed to get changes by user ${user}`, error);
        return {
          content: [
            {
              type: 'text',
              text: `Error getting changes by user "${user}": ${error.message}`,
            },
          ],
          isError: true,
        };
      }
    }
    
    private formatChangeResult(change: Change): string {
  • Zod schema defining the input parameter for the tool: projectSlug as a required string.
    parameters: z.object({
      projectSlug: z.string().describe('The slug of the project'),
    }),
  • @Tool decorator that registers the getProjectChanges method as an MCP tool with name, description, and parameters schema.
    @Tool({
      name: 'getProjectChanges',
      description: 'Get recent changes for a specific project',
      parameters: z.object({
        projectSlug: z.string().describe('The slug of the project'),
      }),
    })
Behavior2/5

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

No annotations are provided, so the description carries the full burden. It states 'Get recent changes' but doesn't disclose behavioral traits such as what 'recent' means (e.g., time range, pagination), whether it's read-only, if it requires authentication, or rate limits. This leaves significant gaps for a tool that fetches data.

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 a single, efficient sentence that directly states the tool's purpose without any wasted words. It's appropriately sized and front-loaded, making it easy to parse quickly.

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

Completeness2/5

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

Given the complexity of fetching changes (which could involve time ranges, pagination, or filtering) and the lack of annotations and output schema, the description is incomplete. It doesn't explain what 'recent' entails or what the return values look like, leaving the agent with insufficient context to use the tool effectively.

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?

The input schema has 100% description coverage, with 'projectSlug' documented as 'The slug of the project'. The description adds no additional meaning beyond this, such as format examples or constraints, so it meets the baseline for high schema coverage without compensating value.

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

Purpose4/5

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

The description clearly states the verb 'Get' and the resource 'recent changes for a specific project', making the purpose understandable. However, it doesn't distinguish this tool from sibling tools like 'getChangesByUser' or 'listRecentChanges', which likely serve similar purposes, so it misses full differentiation.

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

Usage Guidelines2/5

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

The description provides no guidance on when to use this tool versus alternatives. With siblings like 'getChangesByUser' and 'listRecentChanges', it's unclear if this tool is for project-specific changes, user-specific changes, or general recent changes, leaving usage ambiguous.

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/mmntm/weblate-mcp'

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