testlink-mcp-server
testlink-mcp-server
MCP (Model Context Protocol) server exposing the TestLink XML-RPC API as typed tools for LLM agents (Claude Desktop, Claude Code, etc.).
Stack
Concern | Choice |
Runtime | Node.js ≥ 18, TypeScript, ESM |
MCP transport |
|
TestLink transport |
|
Schema validation |
|
Tests |
|
Project structure
testlink-mcp-server/
├── src/
│ ├── index.ts # MCP server entry point (registers all tool groups, stdio transport)
│ ├── client.ts # TestLinkClient: XML-RPC wrapper, retry/timeout, error normalization
│ ├── types.ts # TestLinkClientConfig, TestLinkError
│ ├── mcp-helpers.ts # toToolResult(): uniform success/error → MCP content mapping
│ ├── xmlrpc.d.ts # minimal ambient types for the untyped `xmlrpc` package
│ └── tools/
│ ├── projects.ts # list_projects, create_project
│ ├── testsuites.ts # list/create/update/delete_test_suite
│ ├── testcases.ts # read/create/update/delete_test_case, list_test_cases_in_suite
│ ├── customfields.ts # get/update_custom_field_value, list_custom_fields_for_project
│ ├── requirements.ts # list/create/get_requirement, create_requirement_specification, assign_requirements
│ ├── testplans.ts # list/create/delete_test_plan, add_test_case_to_test_plan, get_test_cases_for_test_plan
│ ├── builds.ts # create/list/close_build
│ ├── executions.ts # create/read_test_execution
│ └── diagnostics.ts # list_available_api_methods (system.listMethods introspection)
├── test/client.test.ts
├── package.json
├── tsconfig.json
├── vitest.config.ts
└── .env.exampleInstallation
npm install
npm run buildConfiguration
Copy .env.example to .env and fill in your instance's details:
cp .env.example .envVariable | Required | Description |
| yes | Full XML-RPC endpoint, e.g. |
| yes | Personal API key from TestLink → My Settings → API interface |
| no | Per-call network timeout (default |
| no | Retry attempts on transient network errors only (default |
| no | Base backoff delay, doubled per attempt (default |
TESTLINK_API_KEY is a credential — keep it out of source control (.env is already git-ignored) and out of shared MCP configs where other users could read it.
Running standalone
npm run dev # ts-node style, via tsx
# or
npm run build && npm startThe server speaks MCP over stdio — it has no meaningful output when run directly in a terminal; it's meant to be launched by an MCP host.
Claude Desktop configuration
Add a block like this to claude_desktop_config.json (Windows: %APPDATA%\Claude\claude_desktop_config.json; macOS: ~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"testlink": {
"command": "node",
"args": ["C:/data/mcp-server/testlink/dist/index.js"],
"env": {
"TESTLINK_URL": "http://your-testlink-host/testlink/lib/api/xmlrpc/v1/xmlrpc.php",
"TESTLINK_API_KEY": "your-devkey-here"
}
}
}
}Restart Claude Desktop after editing the config. Run npm run build first — the config points at the compiled dist/index.js, not the TypeScript source.
Tools exposed
Tool | TestLink API method |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| best-effort probe, see caveat below ⚠️ |
|
|
|
|
|
|
|
|
| client-side filter over |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Known API limitations (⚠️ above)
TestLink's official XML-RPC surface is oriented around CI reporting (create projects/suites/cases/plans/builds, report results) and historically has no stock endpoints for update/delete on test suites, test cases, or test plans, nor for closing a build or listing custom field definitions. This server implements those tools anyway, calling the conventionally-named method (tl.updateTestSuite, tl.deleteTestSuite, tl.deleteTestCase, tl.deleteTestPlan, tl.closeBuild), because some installations add them via patches or newer releases.
Before relying on any ⚠️-marked tool, call list_available_api_methods (wraps the standard XML-RPC system.listMethods introspection call) to confirm your TestLink server actually exposes it. If it doesn't, the call fails as a clear XML-RPC "unknown method" fault surfaced through this server's normal error path — not a silent no-op.
get_requirement: TestLink has no single-requirement lookup; this fetches the full project requirement list viatl.getRequirementsand filters client-side by requirement id or doc id. Fine for small/medium requirement sets; not paginated.list_custom_fields_for_project: TestLink's API can only read a named custom field's value on a specific test case — it cannot enumerate a project's custom field definitions. This tool takes a representative test case plus a list of candidate field names (visible in TestLink Admin → Custom Fields) and probes each one, returning only the fields that resolved to a non-empty value. It cannot discover field names you don't already supply.
Error handling
TestLink signals failures two ways, both normalized into a single TestLinkError:
XML-RPC faults — transport-level errors (bad method, malformed params).
In-band error payloads — TestLink often returns HTTP 200 with a body like
[{code: 200, message: "..."}], or{status: false, message: "..."}for write operations.TestLinkClientinspects every response and throwsTestLinkErrorfor both cases, so callers never have to special-case a "successful" HTTP response that's actually a failure.
Every tool handler wraps its call in toToolResult(), so failures come back as an MCP tool result with isError: true and a JSON body {error: true, method, code, message} — never an uncaught exception that kills the server process.
Network-level errors (ECONNRESET, ETIMEDOUT, ECONNREFUSED, ENOTFOUND, EAI_AGAIN, EPIPE) are retried with exponential backoff up to TESTLINK_RETRIES times; logical TestLink API errors are never retried, since retrying a duplicate-name or bad-devKey error just repeats the same failure.
Tests
npm testtest/client.test.ts mocks the xmlrpc module entirely and covers: devKey injection, both TestLink in-band error shapes, the retry path on transient network errors, no-retry on logical errors, system.* calls skipping devKey, and the client-side requirement lookup's not-found path.
Security notes
TESTLINK_API_KEYgrants full API access under whichever TestLink user issued it — treat it like any other credential.If
TESTLINK_URLishttps://, the client usesxmlrpc.createSecureClient; prefer HTTPS whenever your TestLink instance supports it, since the XML-RPC API otherwise sends the devKey in plaintext.All tool inputs are validated against explicit
zodschemas before reaching the TestLink client — malformed input is rejected by the MCP layer rather than forwarded to the XML-RPC endpoint.