Skip to main content
Glama
r-huijts

OpenTK Model Context Protocol Server

by r-huijts

list_persons

Retrieve detailed information on all Members of Parliament, including IDs, names, titles, party affiliations, and faction memberships. Use this to analyze parliamentary composition, create reports, or find MP details for further queries.

Instructions

Provides a complete directory of current Members of Parliament with their IDs, names, titles, party affiliations, and faction memberships. The response is a JSON array where each entry contains an MP's full details. Use this tool when a user needs comprehensive information about all MPs, wants to analyze the composition of parliament by party, or needs to find specific MPs by name or party. This tool is particularly useful for creating reports about parliamentary representation or for finding the IDs of MPs that can be used with other tools like 'get_photo'. This tool takes no parameters as it returns all current MPs. For a more targeted approach when looking for specific MPs, consider using the 'search_tk' tool with the MP's name.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • src/index.ts:78-106 (registration)
    Registration of the 'list_persons' MCP tool, including tool description, empty input schema, and handler function that calls apiService.getPersons() to retrieve and return the list of MPs as JSON.
    mcp.tool(
      "list_persons",
      "Provides a complete directory of current Members of Parliament with their IDs, names, titles, party affiliations, and faction memberships. The response is a JSON array where each entry contains an MP's full details including unique MP IDs that can be referenced by other tools. This tool takes no parameters as it returns all current MPs. Applicable when comprehensive information about all MPs is needed or when analyzing the composition of parliament by party.",
      {},
      async () => {
        try {
          // Use the tkconv API to get the MP list
          const persons = await apiService.getPersons();
    
          if (persons.length === 0) {
            return {
              content: [{
                type: "text",
                text: "No MPs found or there was an error retrieving the MP list. Please try again later."
              }]
            };
          }
    
          return { content: [{ type: "text", text: JSON.stringify(persons, null, 2) }] };
        } catch (error: any) {
          return {
            content: [{
              type: "text",
              text: `Error fetching MP list: ${error.message || 'Unknown error'}`
            }]
          };
        }
      }
    );
  • The getPersons() method in ApiService class, which fetches the kamerleden.html page and extracts the list of persons using the private extractPersonsFromHtml method.
    async getPersons(): Promise<any[]> {
      try {
        // Fetch the HTML page that contains the MP list
        const html = await this.fetchHtml("/kamerleden.html");
    
        // Extract MP data from the HTML
        const persons = this.extractPersonsFromHtml(html);
    
        return persons;
      } catch (error) {
        return [];
      }
    }
  • Private helper method extractPersonsFromHtml that uses regex to parse the HTML table from kamerleden.html, extracting MP details like ID, full name (split into first/last), party/fractie, and constructs standardized person objects.
    private extractPersonsFromHtml(html: string): any[] {
      const persons: any[] = [];
    
      try {
        // Extract the table rows containing MP data
        const tableRegex = /<table[^>]*>[\s\S]*?<tbody>([\s\S]*?)<\/tbody>/gi;
        const tableMatch = tableRegex.exec(html);
    
        if (!tableMatch || !tableMatch[1]) {
          return [];
        }
    
        const tableContent = tableMatch[1];
    
        // Extract each row (MP) from the table
        const rowRegex = /<tr[^>]*>([\s\S]*?)<\/tr>/gi;
        let rowMatch;
    
        while ((rowMatch = rowRegex.exec(tableContent)) !== null) {
          if (!rowMatch[1]) continue;
          const rowContent = rowMatch[1];
    
          // Extract the MP ID from the link
          const idRegex = /persoon\.html\?nummer=(\d+)/i;
          const idMatch = rowContent.match(idRegex);
          const id = idMatch?.[1] ? parseInt(idMatch[1], 10) : null;
    
          if (!id) continue;
    
          // Extract the MP name
          const nameRegex = /<a[^>]*>([\s\S]*?)<\/a>/i;
          const nameMatch = rowContent.match(nameRegex);
          const fullName = nameMatch?.[1]?.trim() || "";
    
          // Split the name into first and last name (simple approach)
          const nameParts = fullName.split(" ");
          const firstName = nameParts.length > 1 ? nameParts[0] : "";
          const lastName = nameParts.length > 1 ? nameParts.slice(1).join(" ") : fullName;
    
          // Extract the party
          const partyRegex = /<td[^>]*>([\s\S]*?)<\/td>/gi;
          const cells = [];
          let cellMatch;
    
          while ((cellMatch = partyRegex.exec(rowContent)) !== null) {
            if (cellMatch[1]) {
              cells.push(cellMatch[1].trim());
            }
          }
    
          // Party is typically in the second or third cell
          const party = cells.length > 2 ? cells[2] : (cells.length > 1 ? cells[1] : "");
    
          // Create the person object
          persons.push({
            Id: id,
            Persoonsnummer: id,
            Voornaam: firstName,
            Achternaam: lastName,
            Fullname: fullName,
            Fractie: party,
            FractieAfkorting: party,
            Functie: "Tweede Kamerlid", // Assuming all are MPs
            Links: {
              self: `/persoon.html?nummer=${id}`
            }
          });
        }
    
        return persons;
      } catch (error) {
        return [];
      }
    }
Behavior4/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 discloses key behavioral traits: the tool returns a complete directory (implying no pagination or limits), specifies the output format ('JSON array'), and mentions use cases like creating reports or finding IDs for other tools. However, it doesn't address potential rate limits, authentication needs, or data freshness, leaving some gaps for a tool with no 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 well-structured and front-loaded: the first sentence states the core purpose, followed by usage guidelines and alternatives. Every sentence adds value—explaining output format, use cases, parameter absence, and sibling tool differentiation—with no redundant information. It efficiently covers all necessary aspects in a compact form.

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 (0 parameters, no output schema, no annotations), the description is mostly complete: it covers purpose, usage, output format, and parameter semantics. However, without annotations or an output schema, it could benefit from more detail on behavioral aspects like error handling or data updates. It adequately compensates for the lack of structured data but has minor gaps.

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?

The tool has 0 parameters, and schema description coverage is 100% (empty schema). The description adds value by explicitly stating 'This tool takes no parameters as it returns all current MPs,' which clarifies the absence of inputs beyond what the schema indicates. Since there are no parameters, a baseline of 4 is appropriate, and the description reinforces this clearly.

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's purpose: 'Provides a complete directory of current Members of Parliament with their IDs, names, titles, party affiliations, and faction memberships.' It specifies the verb ('Provides'), resource ('directory of current Members of Parliament'), and output format ('JSON array'), and distinguishes it from sibling tools like 'search_tk' by emphasizing it returns all MPs without filtering.

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 when to use this tool: 'when a user needs comprehensive information about all MPs, wants to analyze the composition of parliament by party, or needs to find specific MPs by name or party.' It also provides clear alternatives: 'For a more targeted approach when looking for specific MPs, consider using the 'search_tk' tool with the MP's name.' This includes both usage scenarios and exclusions.

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

Related 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/r-huijts/opentk-mcp'

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