@snowluma/mcp
v1.14.22
Published
MCP server exposing the SnowLuma OneBot action catalog (docs + JSON Schemas) and optional execution to LLM clients.
Maintainers
Readme
@snowluma/mcp
An MCP server for the SnowLuma OneBot action catalog. It runs in two modes from one binary:
- docs (default) — read-only: every action's docs, parameters, cross-field
constraints, and a ready-to-use JSON Schema, so an LLM can answer "what
params does
set_group_bantake?" without holding the whole catalog in context. - execution (opt-in) — when pointed at a running OneBot HTTP endpoint, the LLM can also call actions: read-only ones freely, write ones behind a gate.
Docs only (default)
Add to your MCP client (Claude Desktop, Cline, …) — no endpoint, no execution:
{
"mcpServers": {
"snowluma": { "command": "npx", "args": ["-y", "@snowluma/mcp"] }
}
}Docs tools
list_actions({ category? })— lightweight index (name / category / summary / aliases /readOnly/stream).get_action({ name })— full doc for one action incl.inputSchemaandreadOnly(accepts aliases).search_actions({ query })— fuzzy match over name / summary / aliases.list_categories()— categories and their action counts.
Also exposes the whole catalog as a resource: snowluma://onebot/actions.
Execution (opt-in)
Point the server at a running SnowLuma instance's OneBot HTTP endpoint (the
httpServer network adapter) and it gains two execution tools:
query_action({ action, params? })— calls a read-only action (e.g.get_*,can_*) and returns the full OneBot response. AnnotatedreadOnlyHint. Refuses write actions (points you toinvoke_action).invoke_action({ action, params?, execution? })— calls any known action, including ones with side effects (send a message, change a group, …). AnnotateddestructiveHintopenWorldHint. Only available in write mode. For automatic local-file upload, passexecution: { input_file: "/path/on/the/mcp/host" }withaction: "upload_file_stream"; this option is never forwarded as OneBot params.
Ordinary actions pass the OneBot envelope through verbatim — a logical failure
(retcode≠0) comes back as data with its wording. Stream protocol failures are
reported as MCP errors because they mean no complete stream result was committed.
Configuration (env)
| Variable | Meaning |
| --- | --- |
| SNOWLUMA_MCP_ENDPOINT | OneBot HTTP endpoint, e.g. http://127.0.0.1:3000/. Absent → docs-only (execution tools hidden). |
| SNOWLUMA_MCP_TOKEN | Access token (sent as Authorization: Bearer …), if the endpoint requires one. |
| SNOWLUMA_MCP_TIMEOUT_MS | Ordinary-action request timeout; for Stream Actions, connection timeout and per-read idle timeout (the idle deadline resets after every body read). Default 30000. Invalid values fail startup. |
| SNOWLUMA_MCP_MODE | docs | read | write. Default: read when an endpoint is set, else docs. Invalid values fail startup. |
| SNOWLUMA_MCP_STREAM_DIR | Directory on the MCP host for completed Stream Action downloads. Created and validated at startup. Default: os.tmpdir()/onebot-mcp-downloads. |
| SNOWLUMA_MCP_UPLOAD_ROOT | Existing directory on the MCP host from which automatic uploads may read. Automatic upload is disabled when absent. |
| SNOWLUMA_MCP_MAX_STREAM_BYTES | Per-file download/upload ceiling in bytes. Positive integer up to 4 GiB; default 4294967296. Invalid values fail server startup. |
Read mode — the LLM can query read-only actions, but cannot perform any write:
{
"mcpServers": {
"snowluma": {
"command": "npx",
"args": ["-y", "@snowluma/mcp"],
"env": {
"SNOWLUMA_MCP_ENDPOINT": "http://127.0.0.1:3000/",
"SNOWLUMA_MCP_TOKEN": "your-access-token"
}
}
}
}Write mode — also enables invoke_action (the bot can send messages, manage
groups, etc.). Enable deliberately:
{
"mcpServers": {
"snowluma": {
"command": "npx",
"args": ["-y", "@snowluma/mcp"],
"env": {
"SNOWLUMA_MCP_ENDPOINT": "http://127.0.0.1:3000/",
"SNOWLUMA_MCP_TOKEN": "your-access-token",
"SNOWLUMA_MCP_MODE": "write",
"SNOWLUMA_MCP_STREAM_DIR": "/var/tmp/onebot-mcp-downloads",
"SNOWLUMA_MCP_UPLOAD_ROOT": "/home/me/mcp-share"
}
}
}
}Stream Actions
The existing query_action / invoke_action tools automatically select the
ordinary or Stream Action HTTP transport from the generated catalog. No separate
MCP tool is needed.
- File downloads (
download_file_stream,download_file_image_stream, anddownload_file_record_stream) are parsed frame-by-frame. Base64 chunks are decoded directly into a randomly named.partfile under the controlled download directory, hashed incrementally, and atomically renamed only after a validfile_completeterminal frame. The MCP result contains onlyfile_path,file_size,sha256,frame_count, andterminal; the file path is on the MCP host. - A client admits at most four top-level active Stream Actions. Download files
share a directory quota of
2 * SNOWLUMA_MCP_MAX_STREAM_BYTES; an unknown-size download reserves the full per-file limit while active. Successfully committed files are not automatically deleted by MCP and continue consuming quota; the operator must move or delete them when no longer needed. - Non-file streams such as
test_download_streamreturn a bounded frame summary. Frame count, summary text, individual wire-frame size, total file bytes, and file chunk count are capped. A malformed frame, disconnect, timeout, size violation, caller cancellation, idle timeout, or invalid terminal cancels the response and removes the partial file.file_chunk/file_completeframes are never admitted without a precedingfile_infoheader. - Automatic upload reads
execution.input_fileonly after resolving it insideSNOWLUMA_MCP_UPLOAD_ROOT(symlink escapes are rejected), hashes and sends it in fixed-size chunks, and asks SnowLuma to verify the final SHA-256. Transfer fields such asstream_id,chunk_data, andchunk_indexare MCP-owned and must not also be supplied inparams. The returnedfile_pathis on the SnowLuma/OneBot host and can be passed to a normal send-file action. - Automatic upload reset is best-effort before commit. If SnowLuma commits
the file but the
file_completeresponse is lost, the upload state has already gone and reset cannot remove that committed file. Keepfile_retentionnon-zero (the default is300000ms) so SnowLuma eventually reclaims it. - Stream Actions remain classified as write operations by the current catalog,
so they require
SNOWLUMA_MCP_MODE=writeandinvoke_action.
Example automatic upload:
{
"action": "upload_file_stream",
"params": { "file_retention": 300000 },
"execution": { "input_file": "/home/me/mcp-share/report.pdf" }
}Safety model
- Read/write is classified per action in the source specs (by what the action
actually does, not its name) and baked into the catalog. The default is write:
an action is callable via
query_actiononly if it is explicitly read-only. - The mode gate is enforced on every call, not just by hiding tools — calling
invoke_actionoutside write mode is refused even if a client sends it directly. - Unknown actions are rejected by both tools (only catalog actions are callable), so typos and non-catalog internal actions can't be driven.
- Stream downloads never put file Base64 into the model result. Downloads use a
.part+ atomic-rename commit; automatic uploads require an explicit realpath- fenced root and attempt a pre-commit remote reset if transfer fails. - A well-behaved client can auto-approve
query_action(read-only) and prompt forinvoke_action(destructive) using the MCP tool annotations.
How it stays in sync
The catalog — including each action's readOnly flag — is a build-time snapshot
generated from @snowluma/onebot's live action specs (collectActionDocs()) on
every build, so it auto-tracks action add/remove and read/write reclassification.
The snapshot is pinned to the SnowLuma version it was built from; a new SnowLuma
release republishes a fresh catalog.
This package is generated; do not hand-edit src/generated/catalog.ts.
