fitnesse-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@fitnesse-mcpRun the SmokeTest suite and show me the results"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
fitnesse-mcp
An MCP server that exposes FitNesse's REST responders as tools, letting an MCP client read wiki pages, run tests and suites, manage the files section, and inspect test history on a FitNesse instance.
Copyright (c) 2026 netcare GmbH. Released under the MIT License.
Requirements
VS Code with the Dev Containers extension, plus Docker — the devcontainer supplies the Python toolchain and dependencies
A reachable FitNesse instance
Working outside the devcontainer? You'll need Python 3.12+ and FastMCP 4, which is currently a prerelease and must be pinned exactly:
pip install "fastmcp==4.0.0b2"Using uv? fastmcp is a thin wrapper that depends on fastmcp-slim at the same
version, and uv only allows prereleases for packages you name explicitly:
[project]
dependencies = ["fastmcp==4.0.0b2"]
[tool.uv]
constraint-dependencies = ["fastmcp-slim==4.0.0b2"]Pin exactly, not
>=4.0.0b1. Each beta in the v4 line has carried breaking changes. This project tracks the prerelease and the pin will move at GA.
Related MCP server: JSON Path MCP Server
Quickstart
1. Open the project in its devcontainer.
git clone https://github.com/netcare-io/fitnesse-mcp.git
cd fitnesse-mcp
code .VS Code detects .devcontainer/ and prompts to reopen in the container — accept
it, or run Dev Containers: Reopen in Container from the command palette
(F1). The first build takes a few minutes; later starts are quick. Python and
all dependencies are installed inside the container, so there's nothing to set
up on your host.
2. Point the server at your FitNesse instance.
Run the remaining commands in the container's terminal:
export FITNESSE_BASE_URL=http://your-fitnesse-host:8080
export FITNESSE_READONLY=1 # recommended for a first run
fastmcp run server.pylocalhost in that URL refers to the container, not your host — see
Troubleshooting if the connection is refused.
Security
This server gives an LLM client the ability to delete pages, purge test history, and roll back versions on your FitNesse instance. Two controls limit that, and both are opt-in:
Start with
FITNESSE_READONLY=1. This hides every write, execute, and control tool, leaving only the read-only tools (see Tools for counts). Open it up deliberately once you know which operations you actually want the model to perform.fitnesse_shutdownis not registered at all unlessFITNESSE_ALLOW_SHUTDOWNis set. It stops the FitNesse server.
Three further notes:
fitnesse_list_files,fitnesse_create_dir,fitnesse_upload_file,fitnesse_download_file,fitnesse_delete_file, andfitnesse_rename_fileare not registered at all unlessFITNESSE_FILES_ROOTis set.Every files tool is confined to the resource root named by
FITNESSE_FILES_ROOTon the remote FitNesse instance (defaultfiles, but some instances configure a different root):files_pathmust start with that root and may not contain.., andfilename/dirname/new_namemust be plain names without path separators. A call aiming outside it (e.g.files_path: "FrontPage") is rejected before any request goes out, so these tools cannot reach wiki pages or other responders. Neitherfitnesse_upload_filenorfitnesse_download_filetouches local disk: content travels through the tool call itself, so both work even when the MCP server and the calling client don't share a filesystem.fitnesse_download_filereturns the file's content directly in its result — as an inline image forimage/*content, or as a text/binary resource otherwise.fitnesse_upload_filetakes the content as an argument — plain text viacontent, or base64-encoded bytes viacontent_base64— instead of a local file path.Credentials in
claude_desktop_config.jsonare stored in cleartext. For shared machines, prefer the HTTP pattern below with the credentials in the server's own environment.
Environment variables
Variable | Default | Description |
|
| FitNesse server base URL |
| (none) | Basic Auth username |
| (none) | Basic Auth password |
|
|
|
|
|
|
|
|
|
| (none — files tools disabled) | Resource root of the files section on the FitNesse instance (e.g. |
|
| Responses above this are truncated and flagged |
|
| Uploads above this are rejected; downloads above this are truncated and rejected |
Tools
23 tools by default, each mapping to one FitNesse responder. Set
FITNESSE_FILES_ROOT to the files-section resource root on your FitNesse
instance to also expose fitnesse_list_files, fitnesse_create_dir,
fitnesse_upload_file, fitnesse_download_file, fitnesse_delete_file, and
fitnesse_rename_file (29 total),
FITNESSE_COMPLETE_TOOLSET=1 to expose 14 additional lower-traffic tools, and
FITNESSE_ALLOW_SHUTDOWN=1 to also expose fitnesse_shutdown (44 with all
four). Under FITNESSE_READONLY, only the read tools are exposed — 9 by
default, or up to 24 with FITNESSE_FILES_ROOT and FITNESSE_COMPLETE_TOOLSET=1
both set.
Pages — read
Tool | Responder |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Pages — write
Tool | Responder |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Tests
Tool | Responder | Mode |
|
| execute |
|
| execute |
|
| read |
|
| control |
|
| control |
History & versions
Tool | Responder | Mode |
|
| read |
|
| read |
|
| read |
|
| read |
|
| read |
|
| write |
|
| write |
Search
Tool | Responder |
|
|
|
|
|
|
|
|
Files section
Tool | Responder | Mode |
|
| read |
| (direct file GET, returned inline as image/text/binary) (needs | read |
|
| write |
|
| write |
|
| write |
|
| write |
Flag-style FitNesse inputs are supported by passing None as a query param
value — for example {"nohistory": None} produces ?nohistory.
Connecting an MCP client
Pattern 1 — stdio (simple, local)
The client launches the server as a subprocess and talks over stdin/stdout. No server process to manage.
{
"mcpServers": {
"fitnesse": {
"command": "fastmcp",
"args": ["run", "server.py"],
"env": {
"FITNESSE_BASE_URL": "http://your-fitnesse-host:8080",
"FITNESSE_USERNAME": "your-username",
"FITNESSE_PASSWORD": "your-password",
"FITNESSE_READONLY": "1"
}
}
}
}Running the server from a Docker image instead? -i keeps stdin open, and each
-e forwards one variable from the env block into the container. Every
variable you set in env needs its own -e flag — anything missing here is
silently ignored inside the container:
{
"mcpServers": {
"fitnesse": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"-e", "FITNESSE_BASE_URL",
"-e", "FITNESSE_USERNAME",
"-e", "FITNESSE_PASSWORD",
"-e", "FITNESSE_READONLY",
"fitnesse-mcp:latest"
],
"env": {
"FITNESSE_BASE_URL": "http://your-fitnesse-host:8080",
"FITNESSE_USERNAME": "your-username",
"FITNESSE_PASSWORD": "your-password",
"FITNESSE_READONLY": "1"
}
}
}
}Pattern 2 — HTTP (shared, production)
Use this when several clients share one server instance, or when the server runs in Docker. Credentials live in the server's environment rather than in each client's config.
FITNESSE_BASE_URL=http://your-fitnesse-host:8080 \
FITNESSE_USERNAME=your-username \
FITNESSE_PASSWORD=your-password \
fastmcp run server.py --transport httpListens on port 8000 by default; override with --port.
Most clients can point at the URL directly:
{
"mcpServers": {
"fitnesse": { "url": "http://localhost:8000/mcp" }
}
}For clients that only speak stdio, fastmcp run <url> proxies to it (internally).
Pattern 3 — devcontainer via docker exec
If you already keep the project's devcontainer running (it's named
fitnesse-mcp-devcontainer, see .devcontainer/devcontainer.json), an MCP
client can attach to it directly instead of spinning up a separate image:
{
"mcpServers": {
"fitnesse": {
"command": "docker",
"args": ["exec", "-i", "fitnesse-mcp-devcontainer", "fastmcp", "run", "server.py"]
}
}
}FITNESSE_* variables aren't passed on this command line — docker exec
inherits whatever environment is already baked into the container.
devcontainer.json's containerEnv block sets them from your host
environment (${localEnv:FITNESSE_BASE_URL} etc.) when the container is
created; export the vars on your host and rebuild the devcontainer for
changes to take effect, or edit the defaults in containerEnv directly.
If you'd rather proxy to an already-running HTTP server on port 8000 inside
the container (Pattern 2's stdio proxy), swap the last three args for run http://127.0.0.1:8000/mcp — but note nothing starts that server
automatically; you'd still need to run fastmcp run server.py --transport http inside the container yourself first.
Interactive testing
Create fastmcp.json file.
./scripts/run-inspector.shOpen the exact URL printed in the terminal — it carries a
?MCP_INSPECTOR_API_TOKEN=... query parameter.
Troubleshooting
401 Unauthorized — either FITNESSE_USERNAME/FITNESSE_PASSWORD are
wrong, or FitNesse isn't configured for authentication and is rejecting the
header. Confirm with curl -u user:pass "$FITNESSE_BASE_URL/FrontPage?responder=raw".
Connection refused — check FITNESSE_BASE_URL. Inside a container,
localhost is the container, not your host; use host.docker.internal (Docker
Desktop) or the host's LAN address.
A write tool is missing — FITNESSE_READONLY is set. Note that any value
other than 1/true/yes/on counts as unset.
"truncated": true in a response — the body exceeded
FITNESSE_MAX_RESPONSE_BYTES and was cut. Common on suite runs with
includehtml. Raise the limit or narrow the request.
Invalid path — the page path contained ?, #, .., or a null byte.
FitNesse page paths are dotted (FrontPage.MySuite.MyTest) with no leading
slash.
ImportError on startup — almost certainly the FastMCP version. This
project targets 4.0.0b2 exactly; v3 and the v4 alphas will not import.
Development
pip install -e ".[dev]"
pytest tests/The test suite runs the server in-process via fastmcp.Client, so it also
verifies the pieces that only fail at call time: dependency injection of
timeouts, tag-based tool visibility, and path-injection rejection. Run it after
any FastMCP version bump — it doubles as the upgrade tripwire.
Devcontainer
The devcontainer (see Quickstart) builds from a multi-stage
Dockerfile at .devcontainer/Dockerfile:
devcontainer— used by VS Code for developmenttest— installs dev dependencies; used byscripts/release.shto run the test suite without requiring Python on the hostproduction— used by Docker Compose for deployment
Release
Releasing is split into two scripts so cutting a release doesn't require
Docker registry access, and publishing the image doesn't require pushing to
git. scripts/release-and-publish.sh runs both in sequence.
scripts/release.sh — tests, version bump, git tag
Prerequisites: clean working tree, checked out on main; push access to
origin; docker (to run the test suite — no local Python needed);
gh auth login, if you want the GitHub release created automatically.
bash scripts/release.sh 0.2.0This runs, in order:
Builds the
testDocker target and runspytestinside it — aborts the release on failureBumps
versioninpyproject.tomlto0.2.0Commits the bump and tags it
v0.2.0Pushes the commit and the
v0.2.0tag tooriginOpens a GitHub release for
v0.2.0viagh(prints the manual-create link instead ifghisn't installed)
scripts/docker-build-and-publish.sh — build + push the image
Prerequisites: docker login harbor.netcare.local (or export
FITNESSE_MCP_REGISTRY to target a different registry).
bash scripts/docker-build-and-publish.sh 0.2.0Builds the production Docker target tagged 0.2.0 and latest, then
pushes both to harbor.netcare.local/fitnesse-mcp. Run it from the tagged
commit (e.g. right after release.sh, or after git checkout v0.2.0 later);
if the version is omitted it's read from pyproject.toml.
scripts/release-and-publish.sh — both, in one step
bash scripts/release-and-publish.sh 0.2.0Production with Docker Compose
docker compose up --build -dEnv vars come from a .env file alongside docker-compose.yml:
FITNESSE_BASE_URL=http://your-fitnesse-host:8080
FITNESSE_USERNAME=your-username
FITNESSE_PASSWORD=your-password
FITNESSE_READONLY=1
# FITNESSE_ALLOW_SHUTDOWN=1
# FITNESSE_COMPLETE_TOOLSET=1
# FITNESSE_FILES_ROOT=filesThe server is then available at http://localhost:8000/mcp or through the stdio-to-html-proxy for stdio-only clients:
{
"mcpServers": {
"fitnesse": {
"command": "docker",
"args": [
"exec", "-i", "fitnesse-mcp",
"fastmcp", "run", "http://127.0.0.1:8000/mcp"
]
}
}
}This server cannot be deployed
Maintenance
Related MCP Connectors
An MCP server that provides access to Testiny projects, test cases and test runs
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceA modular and extensible tool server built on FastMCP that supports multiple tools organized across files and communicates via MCP protocol.-
- FlicenseNot gradedqualityDmaintenanceMCP server that provides tools to read JSON files and query data using jsonPath expressions.-
- FlicenseAqualityCmaintenanceMCP server for Confluence REST API enabling page retrieval, attachment downloads, space listing, comment access, and full-text search via tools.6-
- FlicenseNot gradedqualityCmaintenanceA lightweight local MCP server built with FastMCP for exposing custom tools to MCP-compatible clients. Enables local development and testing of MCP tools and resources.1-