Skip to main content
Glama

compare-pages

Read-onlyIdempotent

Compare two versions of a wiki page and return a compact text diff. Supports revision IDs, page titles, or wikitext, with an option for cheap change detection.

Instructions

Returns the changes between two versions of a wiki page as a compact text diff. Each side accepts a revision ID, page title (latest revision), or supplied wikitext; text-vs-text is rejected. Only the changes are returned over the wire. For the full text of both sides, fetch with get-page instead. If a title or revision ID does not exist, an error is returned. Set includeDiff=false for a cheap change-detection response that skips diff rendering and returns just the change flag, revision metadata, and size delta. Diff output is truncated at 50000 bytes by default with a trailing marker; a narrower revision range or includeDiff=false avoids truncation.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
fromRevisionNoRevision ID for the "from" side
fromTitleNoWiki page title for the "from" side (latest revision is used)
fromTextNoSupplied wikitext for the "from" side
toRevisionNoRevision ID for the "to" side
toTitleNoWiki page title for the "to" side (latest revision is used)
toTextNoSupplied wikitext for the "to" side
includeDiffNoInclude the diff body (default true). Set false for a cheap change-detection response.

Implementation Reference

  • The main tool definition and handler for 'compare-pages'. The `comparePages` object implements the Tool interface with name 'compare-pages', inputSchema (Zod schema for fromRevision/fromTitle/fromText, toRevision/toTitle/toText, includeDiff), and an async handle() function that validates sides, makes an mwn request to the MediaWiki 'compare' API, and builds the response payload.
    export const comparePages: Tool<typeof inputSchema> = {
    	name: 'compare-pages',
    	description:
    		'Returns the changes between two versions of a wiki page as a compact text diff. Each side accepts a revision ID, page title (latest revision), or supplied wikitext; text-vs-text is rejected. Only the changes are returned over the wire. For the full text of both sides, fetch with get-page instead. If a title or revision ID does not exist, an error is returned. Set includeDiff=false for a cheap change-detection response that skips diff rendering and returns just the change flag, revision metadata, and size delta. Diff output is truncated at 50000 bytes by default with a trailing marker; a narrower revision range or includeDiff=false avoids truncation.',
    	inputSchema,
    	annotations: {
    		title: 'Compare pages',
    		readOnlyHint: true,
    		destructiveHint: false,
    		idempotentHint: true,
    		openWorldHint: true,
    	} as ToolAnnotations,
    	failureVerb: 'compare pages',
    
    	async handle(args, ctx: ToolContext): Promise<CallToolResult> {
    		const sideError = validateSide('from', args) ?? validateSide('to', args);
    		if (sideError) {
    			return ctx.format.invalidInput(sideError);
    		}
    		if (args.fromText !== undefined && args.toText !== undefined) {
    			return ctx.format.invalidInput('Cannot compare supplied text against supplied text');
    		}
    
    		const includeDiff = args.includeDiff ?? true;
    		const mwn = await ctx.mwn();
    		const response = await mwn.request({
    			action: 'compare',
    			prop: includeDiff ? 'ids|title|size|timestamp|diff' : 'ids|title|size|diffsize',
    			formatversion: '2',
    			...buildSideParams('from', args),
    			...buildSideParams('to', args),
    		});
    		// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- mwn API response shape; trusted at this boundary
    		const compare = response.compare as CompareResponse | undefined;
    		if (!compare) {
    			return ctx.format.error(
    				'upstream_failure',
    				'Failed to compare pages: no compare result returned',
    			);
    		}
    		return ctx.format.ok(buildPayload(compare, args, includeDiff));
    	},
    };
  • The inputSchema for compare-pages using Zod validation. Defines seven optional fields: fromRevision, fromTitle, fromText, toRevision, toTitle, toText, and includeDiff. The type ComparePagesArgs is inferred from this schema.
    const inputSchema = {
    	fromRevision: z.number().int().positive().optional().describe('Revision ID for the "from" side'),
    	fromTitle: z
    		.string()
    		.optional()
    		.describe('Wiki page title for the "from" side (latest revision is used)'),
    	fromText: z.string().optional().describe('Supplied wikitext for the "from" side'),
    	toRevision: z.number().int().positive().optional().describe('Revision ID for the "to" side'),
    	toTitle: z
    		.string()
    		.optional()
    		.describe('Wiki page title for the "to" side (latest revision is used)'),
    	toText: z.string().optional().describe('Supplied wikitext for the "to" side'),
    	includeDiff: z
    		.boolean()
    		.optional()
    		.describe(
    			'Include the diff body (default true). Set false for a cheap change-detection response.',
    		),
    } as const;
    
    type ComparePagesArgs = z.infer<z.ZodObject<typeof inputSchema>>;
  • comparePages is imported from './compare-pages.js' and added to the standardTools array at line 50. It is then registered with the MCP server via the register() function in registerAllTools().
    const standardTools: Tool<any>[] = [
    	getPage,
    	getPages,
    	getPageHistory,
    	getRecentChanges,
    	searchPage,
    	searchPageByPrefix,
    	parseWikitext,
    	comparePages,
  • The generic register() function that calls server.registerTool() with the tool's name, description, inputSchema, and annotations. This is how compare-pages gets registered with the MCP SDK.
    export function register<TSchema extends ZodRawShape, TCtx extends ToolContext>(
    	server: McpServer,
    	tool: Tool<TSchema, TCtx>,
    	handler: (args: z.infer<z.ZodObject<TSchema>>) => Promise<CallToolResult>,
    ): RegisteredTool {
    	return server.registerTool(
    		tool.name,
    		{
    			description: tool.description,
    			inputSchema: tool.inputSchema,
    			annotations: tool.annotations,
    		},
    		// The SDK callback signature is `(args, extra) => ...`. Our descriptor
    		// handlers ignore the `extra` parameter, so we widen the type here. The
    		// `ZodRawShape` constraint from zod is the same shape as the SDK's
    		// `ZodRawShapeCompat` (Record<string, AnySchema>) — TypeScript just
    		// can't unify them through the generic boundary.
    		// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- generic boundary; MCP SDK's ToolCallback can't be unified with our typed handler
    		handler as unknown as ToolCallback<TSchema>,
    	);
    }
  • The inlineDiffToText() helper. Parses HTML diff output (from MediaWiki's inline diff format) into a simple text diff with context lines, additions, and deletions. Used by compare-pages to convert the 'body' of the compare response into readable diff text.
    export function inlineDiffToText(html: string): string {
    	if (!html) {
    		return '';
    	}
    
    	const lines: string[] = [];
    	const rowRegex = /<tr\b[^>]*>([\s\S]*?)<\/tr>/gi;
    	let rowMatch;
    
    	while ((rowMatch = rowRegex.exec(html)) !== null) {
    		const cells = extractCells(rowMatch[1]);
    
    		const linenoCell = findCell(cells, 'diff-lineno');
    		if (linenoCell) {
    			const text = stripTags(linenoCell.inner);
    			const m = text.match(/Line\s+(\d+)/i);
    			if (m) {
    				lines.push(`@@ Line ${m[1]} @@`);
    			}
    			continue;
    		}
    
    		const contextCell = findCell(cells, 'diff-context');
    		if (contextCell) {
    			lines.push('  ' + stripTags(contextCell.inner));
    			continue;
    		}
    
    		const deleted = findCell(cells, 'diff-deletedline');
    		const added = findCell(cells, 'diff-addedline');
    
    		if (deleted) {
    			lines.push('- ' + stripTags(deleted.inner));
    		}
    		if (added) {
    			lines.push('+ ' + stripTags(added.inner));
    		}
    	}
    
    	return lines.join('\n');
    }
Behavior5/5

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

Discloses behavioral traits beyond annotations: rejects text-vs-text, returns only changes over the wire, truncates diff at 50000 bytes, and explains how to avoid truncation. No contradiction with readOnlyHint.

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?

Approximately 120 words, front-loaded with main purpose, each sentence adds necessary context, no redundancy, well-structured.

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?

For a tool with 7 parameters and no output schema, it covers input constraints, error conditions, diff truncation, cheap detection option, and comparison with sibling. Lacks explicit output structure but provides sufficient guidance.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, but description adds value by explaining parameter combinations (text-vs-text rejection), the effect of includeDiff (cheap detection, avoids truncation), and error conditions for titles/revisions.

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 it returns the changes between two versions as a compact text diff, specifying accepted inputs (revision ID, title, wikitext) and rejecting text-vs-text. It distinguishes from sibling tool get-page for full text retrieval.

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?

Explicitly says when to use get-page instead for full text, when to set includeDiff=false for cheap detection, warns about errors for non-existent titles/revisions, and explains truncation behavior.

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/ProfessionalWiki/MediaWiki-MCP-Server'

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