npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

testlink-mcp-server

v1.2.0

Published

Production-ready MCP server for TestLink XML-RPC integration.

Readme

TestLink MCP Server

An MCP server that exposes TestLink over its XML-RPC API, so an AI client (Claude Desktop, Claude Code, Cursor) can read and write test cases, suites and execution results by name — no test case IDs, no XML-RPC calls by hand.

Twelve tools cover the day-to-day TestLink loop: browse projects and suites, create and update test cases, read and set custom fields, and report a pass/fail/blocked result against a test plan and build.


How it works

AI client (Claude / Cursor)
  → stdio ──► TestLink MCP server
                 registerTools    tool definitions + input schemas
                 toolController   zod validation, error → MCP result
                 services         one per tool, name → ID resolution
                 gateway          XML-RPC calls, response normalization
                                          │
                                          ▼
                            TestLink xmlrpc.php (XML-RPC v1)

Everything is addressed by name. TestCaseResolutionService turns a project, suite or test case name into the internal TestLink ID, walking the full suite tree breadth-first and caching projects and suites for 60 seconds so a multi-step conversation doesn't re-fetch the tree on every tool call. Name matching is case-insensitive.

TestLink's XML-RPC responses are inconsistent — arrays in some versions, objects keyed by ID in others, and field names that vary (name / testcase_name, tc_external_id / full_tc_external_id). The gateway normalizes all of that, so services and tool output see one stable shape.


Requirements

  • Node.js 18+ (ES2022 / NodeNext modules)
  • A TestLink instance with the XML-RPC API enabled and a developer key for the account the server acts as

Setup

npm install
cp .env.example .env   # then fill in your values
npm run build

Environment Variables

| Variable | Required | Default | Description | |---|---|---|---| | TESTLINK_API_ENDPOINT | Yes | — | TestLink XML-RPC endpoint URL, e.g. https://host/lib/api/xmlrpc/v1/xmlrpc.php. Must parse as a URL. | | TESTLINK_DEV_KEY | Yes | — | TestLink API dev key (My Settings → API interface) | | TESTLINK_AUTHOR_LOGIN | No | admin | Author username for creating/updating test cases | | MCP_SERVER_NAME | No | testlink-mcp-server | MCP server name, also tagged on every log line | | MCP_SERVER_VERSION | No | version from package.json | MCP server version reported in the handshake | | LOG_LEVEL | No | info | fatal error warn info debug trace |

Config is validated with zod at startup — a missing or malformed variable exits immediately with the offending field named, rather than failing on the first tool call.

Keep .env out of git. The developer key grants full API access under that TestLink account.

Run

npm run dev        # tsx, no build step — development
npm run build      # tsc → dist/, then chmod +x dist/index.js
npm start          # node dist/index.js — production
npm run typecheck  # tsc --noEmit

The server speaks stdio, so it is normally launched by the MCP client rather than run by hand.

MCP Client Configuration

  • Command: node
  • Args: dist/index.js

Ensure environment variables are available to the MCP process. Add to your client's MCP config — claude_desktop_config.json for Claude Desktop, .mcp.json for Claude Code, or the equivalent in Cursor:

{
  "mcpServers": {
    "testlink": {
      "command": "node",
      "args": ["/absolute/path/to/TZ-Taazaa-Testlink-MCP-Server/dist/index.js"],
      "env": {
        "TESTLINK_API_ENDPOINT": "https://your-testlink-host/lib/api/xmlrpc/v1/xmlrpc.php",
        "TESTLINK_DEV_KEY": "your-testlink-dev-key",
        "TESTLINK_AUTHOR_LOGIN": "admin"
      }
    }
  }
}

Use an absolute path — the client does not run from this repo's directory. Run npm run build first; dist/index.js is what the config points at. Variables set in env here take precedence over .env.


Available Tools

| # | Tool | Description | |---|---|---| | 0 | get_projects | List all TestLink projects | | 1 | get_test_cases | Fetch test cases with optional filters (suite, importance, automated) | | 2 | create_test_case | Create a test case in a project/suite | | 3 | update_test_case | Update an existing test case (including custom fields) | | 4 | create_test_suite | Create a test suite, optionally nested under a parent | | 5 | get_test_suites | List test suites, optionally filtered by parent | | 6 | get_custom_field | Get a custom field value on a test case | | 7 | update_custom_field | Update custom field values on a test case | | 8 | report_execution | Report test execution result (passed/failed/blocked) for a test case under a test plan and build | | 9 | get_test_plans | List all test plans for a project | | 10 | get_builds | List all builds for a test plan | | 11 | get_platforms | List all platforms for a test plan |

Inputs

Every tool that needs a project takes project_name. If the user hasn't named one, the tool descriptions instruct the model to call get_projects first and ask.

| Tool | Required | Optional | | --- | --- | --- | | get_projects | — | — | | get_test_suites | project_name | parent_suite_name | | create_test_suite | project_name, suite_name | details, parent_suite_name | | get_test_cases | project_name | test_suite_name, importance, automated | | create_test_case | project_name, test_suite_name, test_case_name, summary, steps, expected_results, importance, execution_type | preconditions | | update_test_case | project_name, test_suite_name, test_case_name | new_test_case_name, summary, preconditions, steps, importance, execution_type, custom_fields | | get_custom_field | project_name, test_suite_name, test_case_name, custom_field_name | — | | update_custom_field | project_name, test_suite_name, test_case_name, custom_fields | — | | get_test_plans | project_name | — | | get_builds | project_name, test_plan_name | — | | get_platforms | project_name, test_plan_name | — | | report_execution | project_name, test_plan_name, build_name, test_suite_name, test_case_name, status | platform_name, notes |

Field values and limits

  • importance — low | medium | high
  • execution_type — manual | automated
  • status (report_execution) — passed | failed | blocked
  • steps — array of { actions, expected_results }, 1–100 entries
  • custom_fields — { "FieldName": "value" } string pairs
  • Lengths — names 255 chars, free text 4000, custom field names 64

Notes on specific tools

  • get_test_cases without test_suite_name walks every suite in the project, which on a large project is a lot of XML-RPC round-trips. Pass a suite when you can. importance and automated filter the normalized results after fetching, not in the query.
  • update_test_case is a partial update — send only the fields you want changed. It resolves the test case by name and edits that version in place; it does not create a new version.
  • report_execution requires platform_name when the test plan has platforms assigned; call get_platforms first to find out. Without it TestLink rejects the execution.
  • create_test_suite with parent_suite_name nests the new suite; without it the suite is created at the project root. The suite tree is cached for 60s and is not invalidated on create, so a suite you just made can be reported as NOT_FOUND by the next call until the TTL expires. (TestCaseResolutionService.invalidateSuites() exists for this but is currently never called.)

Response format

Every tool returns a single JSON text block, so results are readable in a transcript and parseable by the client.

Success

{
  "ok": true,
  "data": {
    "count": 1,
    "items": [
      {
        "test_case_id": "1421",
        "external_id": "KIN-42",
        "name": "Validate user can log in",
        "summary": "...",
        "steps": ["Open the login page", "Submit valid credentials"],
        "expected_results": ["Login page renders", "Dashboard loads"],
        "importance": "high",
        "execution_type": "automated",
        "suite_name": "Authentication"
      }
    ]
  }
}

Failure — also flagged with MCP's isError, so the client shows it as a failed call:

{
  "ok": false,
  "error": { "code": "NOT_FOUND", "message": "Test suite not found: Authentcation" }
}

| Code | Means | | --- | --- | | VALIDATION_ERROR | Input failed the tool's zod schema — wrong type, missing field, value outside the allowed set. | | NOT_FOUND | A named project, suite, test case, plan, build or platform doesn't exist. | | CONFLICT | Raised by create_test_case when the suite already holds a test case with that name. | | INTEGRATION_ERROR | TestLink returned an error, or a response the gateway couldn't make sense of. | | INTERNAL_ERROR | Anything else — an unexpected exception. |

Errors never throw out of a tool: executeTool catches everything, logs it with the tool name, and returns the structured error above. A bad call fails that call, not the server.


Architecture

Controller (validation + response) → Service (business logic) → Gateway (XML-RPC adapter)

Folder Structure

src/
  config/          env.ts, logger.ts
  controllers/     toolController.ts
  integration/     testlink/ (mappers, gateway, xmlrpc client)
  services/
    projects/      getProjectsService
    testCases/     create, get, update, customField, resolution, schemas
    testSuites/    create, get
    executions/    reportExecution, getTestPlans, getBuilds, getPlatforms
  shared/          cache, errors, mcpResponse
  tools/           registerTools.ts
  types/           testlink.ts
  index.ts
specs/
  testcase.md      sample BDD test cases, used as input when exercising the server

Adding a tool means: a zod shape in services/testCases/schemas.ts, a service class, a line in index.ts to construct it, and a server.registerTool block in registerTools.ts.

Logs go to stderr via pino — stdout carries the MCP protocol, so writing to it would corrupt the stream. Set LOG_LEVEL=debug to see each suite fetch and the raw TestLink payloads.


Troubleshooting

| Symptom | Cause | | --- | --- | | Exits at startup naming a variable | .env missing or a variable malformed — TESTLINK_API_ENDPOINT must parse as a URL. | | Every call returns INTEGRATION_ERROR | Wrong endpoint path, XML-RPC disabled in TestLink, or an invalid dev key. | | NOT_FOUND on a name you can see in TestLink | Matching is case-insensitive but exact otherwise — no trimming of inner whitespace, no partial match. | | A just-created suite isn't found | Suite and project lookups are cached for 60s and nothing invalidates them on write. Wait out the TTL or restart the server. | | report_execution rejected | The test plan has platforms — pass platform_name (get_platforms lists them). | | Client shows the server as failed to start | The config points at dist/index.js without a build, or at a relative path. |

License

MIT