Model Context Protocol (MCP)#
Use scikitplot.mcp to make documentation searchable from an
MCP-compatible assistant, developer tool, local process, container, or service.
The normal user workflow is intentionally small:
choose how the MCP server should run;
verify it before connecting a client;
optionally point it at your own documentation; and
let the client call the read-only
search_docstool.
You do not need to understand the retrieval internals to get started.
At a glance#
Main tool:
search_docsDefault transport: local
stdioHTTP transport: Streamable HTTP
Default HTTP endpoint:
http://127.0.0.1:8000/mcpDefault health endpoint:
http://127.0.0.1:8000/healthzYour docs: optional UTF-8 JSONL corpus via
--docs-jsonlDefault HTTP behavior: stateless
Safety model: returned document text is explicitly untrusted reference data
Choose your scenario#
I want to… |
Start with… |
Best fit |
|---|---|---|
See whether MCP works |
|
No client and no network required. |
Connect a local assistant |
default |
The MCP client starts the Python process. |
Search a small set of my own docs |
|
Simple local or CI use. |
Connect through localhost HTTP |
|
Local services and HTTP-capable clients. |
Run in Docker |
|
Container-friendly HTTP defaults. |
Add a CI/readiness check |
|
Detect missing, stale, or wrong documentation indexes. |
Serve a team or remote client |
HTTP behind production controls |
Authentication, TLS, network policy, and quotas are required. |
Search a larger or richer corpus |
|
BM25, vector, or hybrid retrieval without changing the MCP tool contract. |
Quick start#
Install the optional MCP server dependency in the environment containing scikit-plots:
python -m pip install "mcp>=2,<3"
First verify the backend without opening a port:
python -m scikitplot.mcp --self-test --self-test-query transport
Then start the default local server:
python -m scikitplot.mcp
Note
The default command uses stdio. It normally waits for an MCP client.
It does not open a browser page, and waiting for the client is expected.
Scenario 1: connect a local assistant#
Use stdio when your MCP client can start a local command for its tools.
This is the simplest mode and does not expose a network listener.
Use these values in the client’s MCP server configuration:
command: python
arguments: -m scikitplot.mcp
The client starts the process and communicates with it over standard input and output.
Tip
Run --self-test from the same Python environment first. This catches an
import, dependency, or documentation-loading problem before the MCP client is
involved.
Scenario 2: test before connecting anything#
The self-test loads the selected retrieval backend, performs one read-only search, validates the output contract, prints JSON, and exits.
python -m scikitplot.mcp \
--self-test \
--self-test-query transport
Require the query to return at least one result:
python -m scikitplot.mcp \
--self-test \
--self-test-query transport \
--self-test-require-match
This is useful for local troubleshooting, Docker image validation, CI, and readiness gates.
Scenario 3: search your own documentation#
For a small local corpus, create a UTF-8 JSON Lines file with one document per
line. doc_id and text are required. title, source_uri, and
anchor are optional but recommended because they improve citations.
{"doc_id":"install","title":"Installation","source_uri":"/install.html","anchor":"install","text":"Install scikit-plots with pip or conda."}
{"doc_id":"plots","title":"Plotting","source_uri":"/plots.html","anchor":"examples","text":"Use plotting helpers to visualize model results."}
Validate the corpus before serving it:
python -m scikitplot.mcp \
--docs-jsonl docs.jsonl \
--self-test \
--self-test-query plotting \
--self-test-require-match
Then start the normal local stdio server with the same corpus:
python -m scikitplot.mcp --docs-jsonl docs.jsonl
Good document IDs are stable and simple, for example:
getting-started, api.metrics.roc, or guide:deployment.
Important
JSONL mode is intentionally bounded and best suited to small and moderate
local corpora. For larger documentation collections, use a production
retrieval backend through scikitplot.mcp rather than continually
growing one in-memory file.
Scenario 4: make CI detect the wrong corpus#
A health endpoint can tell you that a process is alive. It cannot prove that the expected documentation was loaded.
Add a unique canary document to the corpus, then require that exact document in a self-test:
python -m scikitplot.mcp \
--docs-jsonl docs.jsonl \
--self-test \
--self-test-query MCP_CANARY_7F3A91C2 \
--self-test-require-match \
--self-test-expected-doc-id scikitplot-canary-001
This helps detect:
a stale image;
the wrong mounted corpus;
an incomplete build;
a broken index; or
a deployment that is healthy at HTTP level but functionally wrong.
For an immutable corpus and deterministic backend, repeated self-tests with the same input should also produce stable results.
Scenario 5: run a local HTTP endpoint#
Use Streamable HTTP when a local service or client connects over HTTP:
python -m scikitplot.mcp --transport streamable-http
Default local endpoints:
MCP:
http://127.0.0.1:8000/mcpHealth:
http://127.0.0.1:8000/healthz
Probe the running health endpoint:
python -m scikitplot.mcp \
--transport streamable-http \
--probe
The health check confirms that the HTTP service is reachable and returns the
expected minimal health response. Use --self-test when you also need to
check corpus loading and search behavior.
Scenario 6: run in Docker#
Use the explicit Docker profile:
python -m scikitplot.mcp --docker
It selects container-friendly defaults, including Streamable HTTP, a container network bind, and the health route.
For local development, publish the container port only on the host loopback interface when possible:
docker run --rm \
-p 127.0.0.1:8000:8000 \
scikitplot-mcp:latest
Then use:
MCP: http://127.0.0.1:8000/mcp
Health: http://127.0.0.1:8000/healthz
Important
--docker changes deployment defaults. It does not add identity,
authentication, authorization, TLS, firewall rules, or per-user quotas.
For production containers, install a built wheel in a clean runtime image
instead of using an editable -e installation. This avoids build-system work
on import and gives more deterministic startup and rollback behavior.
Scenario 7: serve remote or team clients safely#
Treat remote MCP deployment like any other network service.
A safer production shape is:
MCP client
|
v
authenticated TLS proxy / gateway
|
v
private scikitplot.mcp HTTP service
|
v
read-only documentation retriever
Use deployment controls appropriate to your environment, including:
authenticated client identity;
authorization for the intended users or service accounts;
TLS at the trusted network boundary;
firewall or private-network restrictions;
request and concurrency limits;
logging and monitoring without leaking secrets; and
an explicit update and rollback process for the corpus and runtime image.
The CLI refuses an ordinary unauthenticated non-local HTTP bind unless you make
that choice explicit. Do not use --allow-unauthenticated-remote as a
shortcut for a production security layer.
How a documentation search works#
A client discovers search_docs and calls it with:
queryThe documentation question or search text.
kThe maximum number of passages to return. The default tool value is
5.
The server then:
validates the tool arguments;
bounds the request;
asks the configured read-only retriever for matching documentation;
cleans and bounds returned text;
validates citation links;
returns passages and machine-readable citations; and
marks retrieved material as untrusted reference data.
A normal result therefore carries both useful text and provenance instead of returning an uncited block of generated prose.
When nothing matches, the result is a normal empty search result with a clear message rather than a fabricated documentation passage.
Security by design#
The user-facing tool is intentionally narrow: search documentation and return references. The server does not need write access to perform this workflow.
Protection |
What it means for you |
|---|---|
Read-only, idempotent search tool |
Repeating a documentation search should not modify your project or corpus. |
Untrusted-content label |
Retrieved pages are context, not commands for the model to obey. |
Bounded query and result sizes |
One request cannot ask the tool to emit an unbounded amount of documentation. |
Bounded passage text |
Large source chunks are truncated before they enter the MCP result. |
Closed tool arguments |
Unknown or misspelled |
Citation URL validation |
Unsafe URL forms such as script/data schemes and credential-bearing links are not emitted as trusted citations. |
Bounded concurrency |
The search service limits simultaneous retrieval work and can report that it is busy. |
HTTP request-size limit |
Streamable HTTP accepts bounded request bodies rather than unlimited input. |
Remote-bind guard |
Accidental unauthenticated non-local HTTP exposure is rejected by default. |
Minimal health response |
The public health route does not need to reveal paths, indexes, environment values, or secrets. |
Important
These controls reduce risk but do not make arbitrary retrieved text trustworthy. A malicious or compromised documentation page can still contain semantic prompt-injection text. The client/model must continue treating retrieved passages only as reference material.
Reliable operation#
Use three different checks for three different questions:
Check |
Answers |
Use it for |
|---|---|---|
|
“What configuration will actually run?” |
CLI/environment debugging before startup. |
|
“Can this corpus load and answer a valid search?” |
Builds, CI, deployments, and functional readiness. |
|
“Is the running HTTP service alive?” |
Runtime/container health checks. |
Inspect configuration without starting the server:
python -m scikitplot.mcp --print-effective-config
For Docker-resolved settings:
python -m scikitplot.mcp --docker --print-effective-config
This is especially useful when CLI options and SCIKITPLOT_MCP_* environment
variables are mixed.
Useful runtime controls#
Most users can keep the defaults. These controls are available when deployment requirements change:
Option |
Default |
Purpose |
|---|---|---|
|
|
HTTP bind address. |
|
|
HTTP port. |
|
|
MCP HTTP endpoint. |
|
|
Lightweight HTTP health route. |
|
|
Bound simultaneous searches. |
|
|
Bound HTTP request-body size. |
|
off |
Keep HTTP sessions only when the client/deployment requires them. |
|
|
Control operational logging detail. |
For automated deployments, the same settings can be supplied with
SCIKITPLOT_MCP_* environment variables.
Common problems#
The command appears to hang#
If you ran:
python -m scikitplot.mcp
this is usually normal. stdio mode is waiting for an MCP client. Run
--self-test if you only want to check that the server works.
The health check passes but search is wrong#
/healthz is intentionally a lightweight process/HTTP check. Run a
--self-test against the real corpus, preferably with a canary and
--self-test-expected-doc-id.
My JSONL file is rejected#
Check that:
the file is UTF-8 JSON Lines, not one large JSON array;
every non-empty line is a JSON object;
every document has a non-empty
doc_idandtext;document IDs are unique; and
the corpus stays within the bounded JSONL limits.
No documentation matches#
An empty result is not automatically a server failure. Try a shorter or more
specific term, verify the intended corpus with --self-test, and confirm the
expected document is actually present.
A non-local HTTP bind is refused#
This is a safety guard. Prefer localhost, an isolated container/private network, or a properly authenticated production gateway. Make remote exposure an intentional deployment decision rather than disabling the guard by default.
A production container rebuilds on import#
Do not ship a Meson editable installation in the runtime image. Build a wheel in a builder stage and install that wheel into a clean runtime stage.
Designed to grow without changing the basic workflow#
The simple user contract stays the same even when the retrieval system becomes more capable:
user question
-> search_docs
-> DocsRetriever
-> cited, bounded, untrusted passages
The retriever behind that contract can evolve independently.
- Small local corpus
InMemoryBm25Retrieverprovides a deterministic, dependency-light path for examples, tests, and bounded JSONL documentation.- Keyword-focused production search
Bm25Retrievercan adapt a full-text/BM25 backend for exact API names, flags, identifiers, and error messages.- Semantic search
CorpusAnnoyRetrievercan connect corpus embeddings and an ANN index for concept and paraphrase matching.- Hybrid search
HybridRetrievercan combine multiple retrieval legs with Reciprocal Rank Fusion. A non-strict hybrid configuration can continue using healthy legs if one retrieval backend fails.
This separation is useful for future changes: transport, indexing strategy, embedding model, storage engine, or ranking can evolve without requiring every MCP client to learn a new documentation tool.
Keep the user guide simple#
For normal use, remember this sequence:
1. Self-test
2. Choose stdio or HTTP
3. Connect the client
4. Search documentation
5. Check citations
Everything after that is an optimization or deployment concern.
Where to go next#
Most users can stop here.
For custom retrieval or programmatic integrations, see scikitplot.mcp.
The public API includes DocsRetriever, RetrievedChunk,
InMemoryBm25Retriever, Bm25Retriever, CorpusAnnoyRetriever,
HybridRetriever, and create_server.
For CLI options available in your installed version, use:
python -m scikitplot.mcp --help