mcp-plugin
Standalone MCP gateway — persistent connections to external MCP servers, with a
CLI to configure and maintain them. Ships with Slife
as its built-in MCP plugin but has no dependency on it. Depends only on
fastmcp, mcp, httpx, json5, and aiosqlite; credstore is optional
(secret ${PLACEHOLDER} resolution + OAuth token storage).
Requirements
Requires uv. The uv routes below pin
Python 3.13 (requires-python is >=3.13, but 3.13 is what CI tests, and
uv would otherwise pick the newest 3.14 it finds).
Your external MCP servers need their own runtimes too: each servers.<name>
entry spawns its command: as a subprocess, so the command must be installed
and on PATH (see the config example below). Typical ones are npx
(Node.js), bun / bunx (Bun), and uvx (uv). A server whose command is
missing fails at connect time with an "executable not found" error.
Optional: credstore (mcp-plugin[credstore]) — only needed when a
secret uses a ${PLACEHOLDER} that isn't in your shell env, or you use OAuth
(auth: {type: "oauth"}). Without it, placeholders resolve against the shell
env only, and OAuth fails with a clear error naming the extra.
Install
uv tool install --python 3.13 mcp-plugin # standalone
uv tool install --python 3.13 'mcp-plugin[credstore]' # + credential store (secret placeholders, OAuth)
# or bundled with Slife (its built-in MCP gateway):
uv tool install --python 3.13 git+https://github.com/juzcn/slife.git
pip install mcp-plugin (into a Python 3.13 environment) also works when uv
isn't available. Re-running a uv tool install over an existing install
is a no-op unless you pass --reinstall — use it to upgrade to a new release.
Verify: mcp-plugin
Config
Server definitions live in mcp-plugin.json5, located by precedence:
MCP_PLUGIN_FILE=<path>(env var — Slife exports this to the same directory asslife.json5when it launches the plugin)./mcp-plugin.json5(dev — when the current directory is the Slife source root, i.e. itspyproject.tomlhasproject.name == "slife")~/.mcp-plugin/mcp-plugin.json5(default, standalone use)
This is the same resolution credstore uses for its credentials.crypt.
Secret values — env entries, auth.client_id/client_secret, HTTP
headers, and the embeddings api_key — accept three forms: "" (empty: no
value / no Authorization header), plaintext (used as-is), or a ${VAR}
placeholder resolved at use time in order shell env → credstore → literal
(VAR is both the env-var name and the credstore key). The embeddings
api_key specifically treats an unresolvable placeholder as empty — it never
sends a literal Bearer ${VAR} — while other fields keep the unresolved
${VAR} literal as the last resort. ${VAR} refs may also appear embedded
in args and url (e.g. "--header", "Authorization: Bearer ${GITHUB_TOKEN}").
// mcp-plugin.json5
{
servers: {
filesystem: {
enabled: false, // optional; absent = enabled
command: "npx",
args: [
"-y",
"@modelcontextprotocol/server-filesystem",
".",
],
},
github: {
auto_load: true, // optional; absent/false = on-demand tools
command: "npx",
args: [
"-y",
"anyapi-mcp-server",
"--name", "github",
"--spec", "https://api.github.com/github-raml",
"--base-url", "https://api.github.com",
"--header", "Authorization: Bearer ${GITHUB_TOKEN}",
],
},
remote: {
url: "https://example.com/mcp",
headers: { Authorization: "Bearer ${REMOTE_TOKEN}" },
auth: { type: "oauth", client_id: "${OAUTH_CLIENT_ID}" },
},
},
// Optional — semantic tool search. Present + base_url ⇒ active; absent ⇒
// mcp_tool_search falls back to keyword/grep. Point it at any OpenAI-
// compatible /v1 endpoint (the local-embed plugin serves this).
embeddings: {
base_url: "http://127.0.0.1:8000/v1",
api_key: "local", // optional; "" | plaintext | "${VAR}" (env → credstore)
model: "bge-m3", // optional; endpoint's active model used otherwise
},
}
The command/args pairs above are spawned as subprocesses on this machine,
so install the runtime each command needs (npx → Node.js, bun/bunx →
Bun, uvx → uv); see Requirements.
Semantic search & automatic degradation
mcp_tool_search defaults to mode="hybrid" — it merges semantic hits with
keyword hits only when the embeddings section is configured and the
backend is actually working. In every other situation it degrades
automatically to keyword search (fts5 BM25, with a LIKE substring
fallback for CJK) — search never fails because embeddings is absent or
broken:
- embeddings not configured (no
embeddingssection) →fts5 - embeddings misconfigured (
base_urlplaceholder, wrong endpoint, bad auth) →fts5 - embeddings endpoint unreachable at search time (was reachable at
startup, is not now) →
fts5 - embedding model failed to load →
fts5 - semantic index still building/rebuilding →
fts5, upgrading back tohybridautomatically when indexing finishes
mode="grep" (exact substring) never involves embeddings and always works.
The result's mode field reports what actually ran (hybrid / fts5 /
grep) and a hint names the reason, so a caller can always tell search
degraded. Enable semantic search by fixing the embeddings section and
running mcp-plugin build (there is no MCP tool for it).
On-demand vs auto-load
External MCP tools are on-demand by default: the host does not bulk-register
them into the LLM's tool list. The agent discovers them with mcp_tool_search,
loads one into the tool list with mcp_tool_load, and releases them at server
granularity (mcp_remove / mcp_set_enabled(false)).
Set auto_load: true on a server to keep the old behavior — its tools are
bulk-registered whenever the server connects.
Enable/disable is always batch at the server level (mcp_set_enabled) —
there is no per-tool toggle. A disabled server's tools are indexed but marked
disabled in the catalog, refused at call time, and skipped by mcp_tool_load.
Tool catalog (mcp-plugin.db)
Every connected server's tools are indexed into a SQLite DB (mcp-plugin.db,
next to the config, or $MCP_PLUGIN_DB): full_name ({server}__{tool}),
name, description, and a per-tool enabled flag. The catalog persists
across restarts, so mcp_tool_search works before any reconnect. Changing
enabled persists too; a disabled tool is refused at call time.
CLI
| Command | Description |
|---|---|
mcp-plugin |
Overview of configured servers |
mcp-plugin set <server> |
Interactive add/configure a server |
mcp-plugin set-embed --base-url <url> [--model <name>] [--api-key <key>] |
Add/update the embeddings section (semantic search) |
mcp-plugin remove <server> |
Remove a server from config (takes effect at next server start) |
mcp-plugin build |
Rebuild the tool catalog DB + index from live connections |
set accepts --transport stdio|http, --command, --url, --args,
--env (KEY=VALUE), --enabled/--no-enabled, and --auth oauth prompts.
set-embed writes/updates the top-level embeddings section: --base-url is
required (a placeholder \${…} or empty value leaves semantic search
disabled, keyword fallback only). --model and --api-key (alias
--apikey) are optional — omit one to keep its current value, pass ""
to clear it. The api_key accepts the same forms as other secrets ("" /
plaintext / ${VAR}) and is stored verbatim. Changes apply at the next
server start, or run mcp-plugin build to (re)index now.
Build
mcp-plugin build reconnects every enabled server, re-syncs its tools into the
catalog, rebuilds the FTS index, and (when an embeddings section is present)
re-embeds the whole catalog. Use it after hand-editing mcp-plugin.json5,
after an external MCP server updates its tools, or after switching the
embeddings model. Unreachable servers are reported, not fatal.
Plugin contract
An MCP-plugin distribution exposes a module that hosts its own FastMCP server
(transport: streamable HTTP on an auto-assigned port, port signaled to stdout as
{"port": N}). Slife discovers it via plugins.external in slife.json5 and
spawns python -m <module>. The management tools (mcp_set, mcp_remove,
mcp_set_enabled, mcp_list, mcp_list_tools, mcp_tool_search,
__mcp_call_tool, __mcp_connection_status,
__mcp_get_tool) are kept separate from the servers they manage.
Tools
mcp_tool_search(query, mode="hybrid", limit, server, include_disabled)— search the catalog.mode:hybrid(semantic + keyword),fts5(BM25), orgrep(exact substring). Hybrid degrades tofts5automatically when semantic search is unavailable — embeddings missing, misconfigured, or still building (see Semantic search & automatic degradation). Results carryfull_name,server,name,description,enabled, snippet and a score;include_disabled=true(default) surfaces disabled tools too.mcp_list_tools(server)— double read: the live connected tools AND the persisted catalog (mcp-plugin.db). When the catalog is unavailable, or its data is out of date vs the live tools (single source of truth = live), the response flags it and suggests runningmcp-plugin buildoffline to rebuild the catalog. While a server is disconnected, the persisted catalog (if any) is still returned; a failed live read instead returns an error ("MCP unavailable") without touching the catalog.mcp_tool_load(full_name)(host side) — register one tool into the LLM's tool list. Refuses disabled tools (a tool is disabled iff its server is disabled — enable/disable is server-level only, viamcp_set_enabled).
Semantic search is configured internally — edit the embeddings section
of mcp-plugin.json5 and run mcp-plugin build to (re)enable indexing; there
is no MCP tool for it.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mcp_plugin-0.1.2.tar.gz.
File metadata
- Download URL: mcp_plugin-0.1.2.tar.gz
- Upload date:
- Size: 102.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2b26d9698e32e2ed102242b8f8076e4675ed017eb8c0fa5ea5924de883b66255
|
|
| MD5 |
68b98652fed6b03246ad633283434ea3
|
|
| BLAKE2b-256 |
916763e80221fe9c5ebda8d0e424881958a599e6959685216c03b0dd606e47f4
|
File details
Details for the file mcp_plugin-0.1.2-py3-none-any.whl.
File metadata
- Download URL: mcp_plugin-0.1.2-py3-none-any.whl
- Upload date:
- Size: 85.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b886c7641951fd914dbbea104f91ff95b6fdedd34f1b832871ee626a63f60933
|
|
| MD5 |
1dc80ed4d6bd0a365b602d65ea1a887e
|
|
| BLAKE2b-256 |
7be348c6063fa2215419323181c0aa130abe9adefa3d758830af247eca1f8ab3
|