Skip to main content

robotframework-mcpclient

A Robot Framework library for testing MCP (Model Context Protocol) servers.

MCP servers are becoming the standard way to give an LLM access to tools, data and prompts — and like any other interface, they need tests. This library lets you write those tests as ordinary Robot Framework test cases: start the server, call its tools, and assert on what it exposes and returns. No async code, no protocol plumbing.

*** Settings ***
Library           MCPClientLibrary
Suite Setup       Connect To MCP Server    python    ${CURDIR}/weather_server.py
Suite Teardown    Disconnect All MCP Servers

*** Test Cases ***
Server Exposes The Weather Tool
    Tool Should Exist                   get_weather
    Tool Should Have Input Schema       get_weather
    Tool Input Schema Should Require    get_weather    city

Weather Tool Answers For A Known City
    ${result}=    Call Tool    get_weather    city=Paris
    Tool Result Should Not Be Error     ${result}
    Tool Result Should Contain Text     ${result}    temperature

Unknown City Is Reported As A Tool Error
    ${result}=    Call Tool    get_weather    city=Nowhereville
    Tool Result Should Be Error         ${result}

Installation

pip install robotframework-mcpclient

Requirements: Python 3.10+, Robot Framework 5.0+, mcp 1.0+

Tested with: Python 3.10–3.13, Robot Framework 5.0.1 / 6.0.2 / 7.5, mcp 2.2.0

Quick test

After installing, verify it works:

python -m robot --version
# Robot Framework 7.5 (Python 3.12.3 on linux)

python -c "from MCPClientLibrary import MCPClientLibrary; print('✓ Library imported')"
# ✓ Library imported

Connecting to a server

Connect To MCP Server starts the server as a subprocess and completes the MCP handshake. The first argument is the executable, the rest are its arguments:

Connect To MCP Server    python    ${CURDIR}/my_server.py
Connect To MCP Server    node     server.js    --verbose
Connect To MCP Server    uv       run    my-server

Extra environment variables and a working directory are optional:

${env}=    Create Dictionary    API_KEY=test-key
Connect To MCP Server    python    server.py    env=${env}    cwd=${CURDIR}

Always close connections in a suite teardown so no server process is left behind between runs:

Suite Teardown    Disconnect All MCP Servers

Several servers at once

Give each connection an alias and switch between them:

Connect To MCP Server    python    weather.py    alias=weather
Connect To MCP Server    python    notes.py      alias=notes

Switch MCP Server        weather
Tool Should Exist        get_weather

Switch MCP Server        notes
Tool Should Exist        create_note

Switch MCP Server changes which connection is current for the whole library — fine sequentially, but calling it from more than one thread races (see Calling several servers concurrently below).

Connecting over HTTP

For a remote MCP server, use Connect To MCP Server Over HTTP instead — same keywords work afterward regardless of transport:

Connect To MCP Server Over HTTP    https://example.com/mcp
Tool Should Exist    get_weather

Pass extra headers for authentication:

${headers}=    Create Dictionary    Authorization=Bearer ${TOKEN}
Connect To MCP Server Over HTTP    https://example.com/mcp    headers=${headers}

What you can test

Tools

Keyword What it does
List Tools Every tool the server exposes
Get Tool Names Just the names, as a list of strings
Get Tool One tool by name
Call Tool Calls a tool with named arguments
Call Tool With Arguments Calls a tool with an argument dictionary
Call Tool On Server Calls a tool on a named connection, without switching (safe for concurrent use)
Get Tool Result Text The text blocks of a result, joined
Get Tool Result Data The structured (JSON) content of a result

Assertions:

Keyword Checks
Tool Should Exist / Tool Should Not Exist The tool is (not) offered
Tool Count Should Be How many tools the server offers
Tool Should Have Input Schema The tool declares a schema at all
Tool Input Schema Should Require The schema marks fields required
Tool Input Schema Should Have Property The schema declares properties
Tool Should Have Description The tool has a non-empty description
Tool Result Should Be Error / Should Not Be Error The tool-level error flag
Tool Result Should Contain Text / Should Not Contain Text Substring in the result text
Tool Result Should Match Regular expression against the result text
Tool Result Should Be Empty / Should Not Be Empty Whether there is content
Tool Result Should Have Data The result carries structured content
Tool Result Should Match Output Schema Structured content matches the tool's declared output schema

Tool call progress

A tool that reports progress during a call — a long-running operation, a multi-step process — has its notifications captured automatically:

${result}=    Call Tool    process_file    filename=data.csv
${progress}=    Get Last Tool Call Progress
Should Be Equal As Numbers    ${progress}[-1][progress]    100
Tool Call Should Have Reported Progress

Get Last Tool Call Progress returns the events from the most recent Call Tool, cleared before each new call.

Server-sent log messages

Set Logging Level    debug
Call Tool    get_weather    city=Nowhereville
Server Should Have Logged    unknown city    level=warning

Set Logging Level, Get Server Log Messages, Clear Server Log Messages, Server Should Have Logged, Server Should Not Have Logged.

Log messages accumulate for the life of the connection; a server must declare the (now-deprecated but widely implemented) logging capability to accept Set Logging Level.

Resources

Resource Should Exist         docs://weather/usage
${text}=    Get Resource Text    docs://weather/usage
Should Contain                ${text}    get_weather

List Resources, Get Resource URIs, Read Resource, Get Resource Text, List Resource Templates, Resource Should Exist, Resource Should Not Exist, Resource Should Contain Text.

Resource subscriptions

Subscribe To Resource    data://counter
Call Tool    bump_counter
Resource Should Have Been Updated    data://counter

Subscribe To Resource, Unsubscribe From Resource, Get Resource Update Notifications, Clear Resource Update Notifications, Resource Should Have Been Updated.

Prompts

Prompt Should Exist               weather_report
Prompt Should Require Argument    weather_report    city
${text}=    Get Prompt Text       weather_report    city=Berlin
Should Contain                    ${text}    Berlin

List Prompts, Get Prompt Names, Get Prompt, Get Prompt Text, Prompt Should Exist, Prompt Should Not Exist, Prompt Should Require Argument.

Client callbacks: roots, sampling, elicitation

MCP lets a server call back into the client mid-tool-call — to ask what directories/URIs the client exposes (roots), to have the client's LLM complete a message (sampling — the pattern behind an agentic tool), or to ask the user a question (elicitation). These keywords script the client's side of that conversation, so a tool that depends on it can be tested without a real LLM or a real user:

${root}=    Create Dictionary    uri=file:///workspace    name=Project
Set Client Roots    ${root}

Set Sampling Response    42
${result}=    Call Tool    agentic_tool    query=what is 6 times 7

${answer}=    Create Dictionary    name=Alice
Set Elicitation Response    accept    ${answer}
${result}=    Call Tool    tool_that_asks_for_a_name

Set Client Roots, Set Sampling Response, Set Elicitation Response.

Sampling and elicitation responses are consumed once, by the next matching request the server sends — set a fresh one before each call that triggers one. If the server asks and nothing is queued, the client answers with a clear error naming which keyword to call, rather than hanging or reusing a stale answer from an earlier test.

Two kinds of failure, and why it matters

MCP has two failure paths, and a test suite needs to tell them apart:

  • A tool error is a normal result with an error flag set — the call succeeded, and the tool is reporting that it could not do the job. Assert on it with Tool Result Should Be Error.
  • A protocol error — an unknown method, a malformed message — fails the keyword outright with a readable message.

Check the tool error flag through the assertion keywords rather than reading the attribute yourself. The field has been spelled isError and is_error across MCP SDK versions; the keywords handle both, so your tests survive an SDK upgrade. This is the main reason to use a library rather than hand-rolling the checks.

Exception types

Robot Framework itself only ever matches error messages, but the exceptions this library raises form a hierarchy, for Python code built on top of it (custom keywords that want to catch a specific failure):

MCPLibraryError
├── MCPTimeoutError        A keyword's timeout expired
├── MCPConnectionError     The connection isn't usable
│   ├── MCPHandshakeError  The server started but initialize() failed
│   └── MCPProcessError    The server process never came up, or crashed mid-call
├── MCPProtocolError       The server returned a JSON-RPC error
└── MCPValidationError     A result didn't match what the server declared

A server that crashes mid-call

If the server process dies while handling a request — not a clean shutdown, an actual crash — the keyword that was waiting on it fails with MCPConnectionError, and the connection is marked closed immediately:

${result}=    Call Tool    tool_that_crashes_the_server
# raises MCPConnectionError: "...connection closed unexpectedly — it may have crashed..."

MCP Server Should Be Connected
# now fails too: the crash already updated the connection's state

Every keyword after that on the same connection fails fast with a clear "not open" message — none of them re-attempt a call against the dead process. To recover, connect again (under the same alias, if you had one):

Connect To MCP Server    python    server.py    alias=myserver
Connect To MCP Server    python    server.py    alias=myserver    # after a crash

Disconnect All MCP Servers is always safe to call in a teardown, even with a crashed connection sitting in the cache alongside healthy ones.

Calling several servers concurrently

Every keyword shares one "current connection", tracked by the library — set by Connect To MCP Server and changed by Switch MCP Server. That's fine called sequentially, which covers ordinary test suites, but it becomes a race if two threads call Switch MCP Server and then act on "the current connection" at the same time: one thread's switch can land between another's switch and its call, and the call goes to the wrong server.

For calls made concurrently from different threads — a custom keyword that spawns threads, say — use Call Tool On Server instead. It names its connection by alias or index directly and never reads or writes the shared "current connection", so there's no shared state for a race to land in:

Connect To MCP Server    python    weather.py    alias=weather
Connect To MCP Server    python    notes.py      alias=notes

${result}=    Call Tool On Server    weather    get_weather    city=Paris
${note}=      Call Tool On Server    notes      create_note     text=remember the milk

Get Last Tool Call Progress takes the same optional connection argument, for reading another connection's progress without switching to it.

Return values

By default keywords return MCP objects, which you reach into with Robot's extended variable syntax:

${result}=    Call Tool    get_weather    city=Paris
Length Should Be    ${result.content}    1

Import the library with convert_results=True to get plain dictionaries and lists instead, which suits data-driven suites and JSON comparison:

Library    MCPClientLibrary    convert_results=True
${result}=    Call Tool    get_weather    city=Paris
Should Be Equal    ${result}[content][0][type]    text

Timeouts

Every keyword waits default_timeout seconds (30 by default) for the server to answer, and each one accepts a timeout that overrides it:

Library    MCPClientLibrary    default_timeout=60
${result}=    Call Tool    slow_tool    timeout=120

A server that stops responding fails one keyword rather than hanging the suite.

Reading the log

Every request and response is written to the Robot log at INFO level, so a failing test shows exactly what was sent and what came back. Large payloads are truncated. Run with --loglevel DEBUG for the full detail.

Keyword documentation

Browse the keywords by category, with examples: robotframework-mcpclient.readthedocs.io/en/latest/keywords.html

Or view the full Libdoc reference — every keyword, every argument: MCPClientLibrary.html

Generate the Libdoc reference locally:

python -m robot.libdoc MCPClientLibrary docs/MCPClientLibrary.html

How the sync/async bridge works under the hood, with diagrams: readthedocs.io/en/latest/architecture.html

Transport support

Two transports are supported, and every keyword after Connect To MCP Server* works the same on both:

  • stdio (Connect To MCP Server) — starts the server as a subprocess. The usual choice for testing a server you're developing locally.
  • Streamable HTTP (Connect To MCP Server Over HTTP) — connects to a running remote server by URL, with optional headers for authentication.

Contributing

The library's own acceptance tests are written in Robot Framework against the sample servers in tests/servers/, so they double as worked examples:

pip install -e ".[dev]"
python -m pytest tests/              # unit tests
python -m robot --outputdir results atest/   # acceptance tests

License

Apache 2.0. See LICENSE.

Release files for robotframework-mcpclient 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for robotframework-mcpclient 0.2.0
File Size Uploaded
robotframework_mcpclient-0.2.0.tar.gz 50.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for robotframework-mcpclient 0.2.0
File Interpreter ABI Platform
robotframework_mcpclient-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 86.1 kB

Release files / robotframework_mcpclient-0.2.0.tar.gz

Download URL robotframework_mcpclient-0.2.0.tar.gz
Size 50.5 kB
Tags Source
SHA-256 checksum
How to use checksums
04e9bedcc9a38b60c001d37dcbaf8ac6488dd4dce885cfcdb388c43738cb9e43
BLAKE2b-256 checksum
How to use checksums
57f495e74b460398211cba5e54b75f6e9bd0dd35d16458c095e5d5e7224af410
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / robotframework_mcpclient-0.2.0-py3-none-any.whl

Download URL robotframework_mcpclient-0.2.0-py3-none-any.whl
Size 35.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0ac6b8aba3c1d3da2aca592f15ea0e33a0ccb6b684febcfdadac3e80bec3e20b
BLAKE2b-256 checksum
How to use checksums
9c88d9eeb454665bc404d9e32bf297f843670b8393c11681ea9332ffcad1edff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page