SnowLuma v2.4.2 source snapshot
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
Publish MCP to npm / Build, test, and publish MCP (push) Waiting to run
Publish SDK to npm / Build, test, and publish SDK (push) Waiting to run
Sync action catalog to docs / sync (push) Waiting to run
Publish UI to npm / Build and publish UI (push) Waiting to run
Build and Release / preflight (push) Waiting to run

This commit is contained in:
TanTan committed 2026-11-13 09:02:00 +08:00
commit fa9c73363d
1367 files changed
+355735

No files matched your search

+391
View File
@@ -0,0 +1,391 @@
# MCP agent features
These additions preserve the existing tool names, action parameters, text results,
and read/write gates. No endpoint is contacted by catalog tools or by a local
runtime overview. The action catalog remains a bundled snapshot, not a claim
about the connected server's capabilities.
## Protocol compatibility and catalog caching
The same `snowluma-mcp` stdio command accepts MCP `2026-07-28` clients and
older clients using the `initialize` handshake. The official server SDK selects
the protocol for the connection. Modern clients can use `server/discover` and
per-request metadata without initialization; legacy clients keep their existing
configuration. This does not add a publicly listening MCP HTTP endpoint.
Modern replies carry the protocol's completion and server metadata. Cache hints
depend on whether a response contains static catalog data or private live state:
| Response | Lifetime | Scope |
| ----------------------------------------------------- | --------------------------------------------- | ------------------------------------------------------------------------------ |
| `tools/list` | Five minutes | `private`; mode and client capabilities affect tool metadata and availability. |
| `resources/list` | Five minutes, or zero when events are enabled | `private`; UI capability and active event resources affect the list. |
| `resources/templates/list` | Five minutes | `public`; the bundled template list is static. |
| `resources/read` for the action catalog or message UI | Five minutes | `public`; these are bundled static assets. |
| `resources/read` for event resources | Zero | `private`; these contain account events and connection state. |
These are client cache hints, not a server-side result cache. Tool calls,
including the runtime overview, never receive these hints. The SDK omits
modern-only result fields for legacy clients. Unsupported protocol revisions
are rejected before action execution; they do not trigger an action retry.
## Call tracing
Every tool invocation gets a W3C trace context. Clients can supply `traceparent`
and optional `tracestate` in the tool request's `_meta`:
```json
{
"name": "query_action",
"arguments": { "action": "get_status" },
"_meta": {
"traceparent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}
}
```
MCP preserves a valid caller trace ID and creates a new span ID for each call.
Without a valid parent it creates an independent trace. The returned
`_meta.traceparent` identifies that call, including tool errors; the existing
text and structured result envelopes remain unchanged. This works with both
protocol generations.
Ordinary requests, streams, automatic upload chunks, and upload cleanup forward
the call context in HTTP headers, never in OneBot action parameters. Concurrent
calls keep independent headers. To follow a call, match its traceparent in MCP
stderr diagnostics with SnowLuma's `http_trace` entry, then follow that entry's
existing `req#` through action lifecycle logs. SnowLuma correlation requires
TRACE logging and a valid authenticated HTTP request; the remote trace ID does
not replace its local request ID. No telemetry exporter is installed.
MCP's call diagnostics contain the known tool name, traceparent, outcome, failure
category and duration, not message contents, tokens, file paths or arbitrary metadata.
`tracestate` is validated with a maximum of 512 characters and 32 members; it is
forwarded but not logged. Invalid trace metadata produces a fixed diagnostic and
does not reject an otherwise valid action. `baggage` is neither forwarded nor
logged. Trace identifiers are correlation data, not authentication or permission.
## Optional execution services
Existing configurations keep working. Input completion and message previews are
additive tools; event connections and durable tasks require explicit configuration.
For example, set these variables in the MCP client's server environment, replacing
addresses, credentials and paths with the intended SnowLuma instance:
```json
{
"SNOWLUMA_MCP_MODE": "write",
"SNOWLUMA_MCP_ENDPOINT": "http://127.0.0.1:3000/",
"SNOWLUMA_MCP_TOKEN": "<your access token>",
"SNOWLUMA_MCP_EVENTS_URL": "ws://127.0.0.1:3001/event",
"SNOWLUMA_MCP_TASK_DIR": "/absolute/private/mcp-tasks",
"SNOWLUMA_MCP_UPLOAD_ROOT": "/absolute/allowed/uploads"
}
```
The HTTP and WebSocket addresses are independently configured server endpoints;
the WebSocket address is never guessed from the HTTP address. Events reuse
`SNOWLUMA_MCP_TOKEN` in an authorization header. Upload and task directories are
on the MCP host. Omit services that are not needed. Docs mode remains offline.
## Completing missing parameters
`query_action_with_input` and `invoke_action_with_input` use the same arguments,
action lookup and mode gates as `query_action` and `invoke_action`. The existing
tools continue to behave exactly as before. The additional tools ask for input
only when a field listed in the action catalog's top-level `required` array is
absent; they do not infer conditional requirements or optional values.
For example, `invoke_action_with_input` with
`{"action":"send_group_msg","params":{"group_id":20001}}` asks for the
missing message. The form contains one `params_json` string, whose contents are
a JSON object such as `{"message":"Hello"}`. The answer is validated against
the missing fields' catalog schemas before normal action execution. Already
provided fields, the action name and execution options cannot be overwritten;
extra answer fields are rejected. A declined, cancelled or invalid answer does
not execute the action.
Clients must advertise form elicitation support. Modern clients receive
`resultType: "input_required"` and resume using the SDK's request-state and input
response mechanism. Legacy clients with elicitation support use the SDK's
`elicitation/create` flow. Clients without form support receive a tool error and
can provide complete arguments to the original tools instead.
Continuation state is signed, tied to the original tool and arguments, expires
after ten minutes and is consumed once before execution. Concurrent or repeated
answers cannot execute the same continuation twice. A runtime holds at most 256
pending prompts; restart invalidates them rather than replaying work. Completed
arguments bypass the input round entirely. This feature is not a general approval
gate and does not add confirmation prompts to existing action calls.
## Live event resources
With `SNOWLUMA_MCP_EVENTS_URL` configured, read and write modes expose
`subscribe_events` and `unsubscribe_events`. The URL must use `ws:` or `wss:`
without embedded credentials, query parameters or fragments. Listing tools or
resources does not connect; only `subscribe_events` opens the event connection.
```json
{
"self_id": 10001,
"types": ["message", "notice"],
"group_ids": [20001],
"user_ids": [30001]
}
```
Use the expected logged-in account for required `self_id`; obtain it through
`get_login_info` if necessary. Every incoming event must match this account.
An account mismatch or malformed event stops the connection and is surfaced.
The optional types are `message`, `notice` and `request`; omitted types mean all
three. `message_sent` is included in the message category. Group and user filters
are combined with AND, so a group filter excludes events without a matching
`group_id`. Each ID filter accepts at most 100 distinct positive integers.
The result contains a canonical `snowluma://onebot/events/<id>` resource URI.
Read it with `resources/read`; use `?after=<next_cursor>` on subsequent reads to
obtain newer buffered entries. Cursors belong to that one resource. The response
includes `status`, `next_cursor`, `oldest_cursor`, `dropped_events`, `gap` and
`notification_failed`. Reads do not consume events. Only events received after
subscription creation are available; this is not message-history storage.
Each resource retains at most 256 events and 1 MiB of event data. One runtime
supports at most 16 resources, sharing one account connection. Incoming frames
are limited to 256 KiB. Buffer eviction and disconnection remain visible through
the cursor/gap and status fields; the service does not silently reconnect or
claim missing events were recovered. Release existing resources before changing
accounts or creating a new connection after disconnection.
For updates, use the **canonical URI without `?after`**:
- Modern clients use `subscriptions/listen` with `resourceSubscriptions: [uri]`
(the TypeScript SDK exposes `client.listen(...)`). Closing that notification
stream does not release the event buffer; call `unsubscribe_events` explicitly.
- Legacy clients first call `resources/subscribe` with the canonical URI, then
receive `notifications/resources/updated`. `resources/unsubscribe` also releases
that event resource. The explicit unsubscribe tool works for both generations.
An update notification indicates that the resource changed; fetch the resource
to read its data. Releasing the last resource closes the WebSocket. Closing the
MCP connection releases its event resources and buffers. No event operation
executes a OneBot business action or sends a QQ message.
## Persistent background transfers
Set an absolute `SNOWLUMA_MCP_TASK_DIR` and use write mode to enable persistent
tasks. Task tools additionally require MCP `2026-07-28` clients advertising
`extensions: {"io.modelcontextprotocol/tasks": {}}`. Legacy clients keep the
existing synchronous execution tools; this server does not expose the obsolete
`tasks/list` or `tasks/result` protocol methods.
`invoke_action_background` takes the same arguments as `invoke_action`, but only
accepts catalog actions marked as streams. For example, it can run
`upload_file_stream` with `execution.input_file` inside the configured upload root.
It writes the task record before starting work and returns a `resultType: "task"`
handle. Ordinary actions continue through the existing tools. The current
background transfer entry does not automatically combine with `_with_input`;
provide the required transfer parameters before starting it.
- `tasks/get` with `{ "taskId": "..." }` returns current state and, when finished,
the saved result or error. A `completed` task can contain a failed business
response: inspect the nested result's diagnostics, `status` and `retcode`.
- `tasks/cancel` requests cooperative cancellation. Cancellation does not undo
remote effects, and a successful completion already in flight may win the race.
Poll the task again for its final state.
- `tasks/update` accepts input responses only for outstanding task input requests.
It is the protocol lifecycle endpoint, not an arbitrary task-editing API. The
current background transfer entry does not itself request interactive input.
- `list_tasks` is an MCP tool returning task metadata without result bodies. It
accepts optional `cursor` and `limit` (1–100; default 50) and returns `nextCursor`
when another page is available.
Task handles advertise `pollIntervalMs: 1000` and `ttlMs: null`. Results survive
restart and are not automatically expired or deleted. Restart marks unfinished
work failed with an explicit **unknown remote outcome** message; it never
replays the operation. This persists transfer status/results, not resumable
upload or download execution. There are at most four concurrent tasks and a
16 MiB limit on each persisted task record.
Storage uses an exclusive runtime lease, private directories/files (0700/0600 on
POSIX) and validated records. Corruption, permission errors and write failures
remain errors. The directory is bound to a digest of the effective mode, endpoint
and token; changing that configuration rejects reuse of the directory. Choose a
new private directory for the new configuration and retain the old history.
Credentials are not stored in the scope record, but task results can contain
private account data and should be protected accordingly.
## Message preview panel
`preview_message` is available in every mode and only constructs a draft:
```json
{
"message_type": "group",
"target_id": 20001,
"text": "Hello",
"reply_to": -123456
}
```
Use `private` for a private conversation. `target_id` must be a positive safe
integer; optional `reply_to` is a non-zero signed 32-bit message ID. The preview
supports literal text and optional quoted replies, not media rendering. Text
that resembles message control codes remains literal text.
Every client receives the same readable text draft, `can_send` flag and
corresponding `invoke_action` arguments. Only clients advertising the MCP Apps
extension `io.modelcontextprotocol/ui` with MIME type
`text/html;profile=mcp-app` receive the tool's UI metadata and listed UI resource.
The panel is a bundled, self-contained HTML asset built with the official Apps
client. It loads no external scripts, fonts or media, opens no network connection
of its own and displays external text without interpreting it as HTML. A missing
packaged asset is a visible packaging error.
Loading the panel sends nothing. Its send button calls the existing
`invoke_action` through the host only after the user clicks, and the server's
write gate still applies. Read/docs mode or a host without server-tool support
keeps sending disabled. The panel prevents duplicate clicks and does not retry
failed or uncertain sends; create a new draft only after checking the outcome.
Successful acknowledgement is not proof of delivery or rendering in QQ.
## Action result schemas and examples
`get_action` and `snowluma://onebot/actions` include `returnsSchema` and
`examples` for every bundled action, including aliases. Both are authored beside
the OneBot action definition and copied into the generated catalog. List/search
responses remain compact; use `get_action` to retrieve the complete contract.
`returnsSchema` describes successful OneBot **`data`**, not the full OneBot
envelope or the MCP `structuredContent` wrapper. Check `status` and `retcode`
before reading it. For stream actions, the schema describes each successful
frame's `data`; MCP downloads instead return saved-file metadata and generic
streams return a frame collection. Error frames and failed envelopes are not
successful results. Unsupported actions use `{ "not": {} }` because they have no
successful result. Compatibility placeholders explicitly document their limits.
Each example has `summary`, `params`, and optional `notes` explaining prerequisites
and identifier sources. These are illustrative calls, never run by catalog tools.
Replace example account/group IDs, opaque handles and paths with values from the
current account before execution. Notes identify operations with multiple steps,
such as completing an upload after all chunks have been acknowledged.
For example, `get_action({ "name": "get_msg" })` documents signed message IDs:
```json
{
"action": "get_msg",
"params": { "message_id": -123456 }
}
```
Pass this object to `query_action` with a real message ID. Writes and streams use
`invoke_action` in write mode. For automatic upload, pass
`{ "action": "upload_file_stream", "execution": { "input_file": "/allowed/hello.txt" } }`
to `invoke_action`, with a real file inside the configured upload root. Ordinary
action paths refer to the SnowLuma server, whereas `execution.input_file` and MCP
download paths refer to the MCP host.
Do not substitute zero for a signed message ID. Resource-only forward uploads
and flash shares have a documented `message_id: 0` sentinel; it cannot be used
for message lookup or recall. Also preserve `null` versus `{}`, optional fields,
and decimal strings such as media `file_size` exactly as documented.
## Structured results
Completed tool results use the advertised `outputSchema` and return
`structuredContent` alongside the existing `content` text block. Modern
`input_required` and background task handles are protocol continuations; inspect
their `resultType` first and obtain the completed result through the relevant
continuation or task lifecycle:
```json
{
"result": { "status": "ok", "retcode": 0, "data": { "message_id": -123 } },
"diagnostics": { "outcome": "success" }
}
```
`result` is the original payload, including arrays such as the category list.
It is `null` for an MCP tool error. `diagnostics.outcome` is `success`, `failed`,
or `unknown`; failures can include `kind`, `message`, and `next_step`.
Ordinary OneBot business failures still return their original envelope without
setting `isError`. Their structured diagnostics indicate failure. An asynchronous
acknowledgement or a response that does not confirm success has outcome `unknown`.
Transport errors and existing tool/stream errors still set `isError: true`.
Error categories describe known causes; unclassified execution errors are not
guessed from message strings. Neither tool execution nor connection checks retry
automatically. Negative message IDs remain valid and are preserved unchanged.
## Search, filters, and pagination
`search_actions` retains its original case-insensitive substring matching and
catalog order unless `match: "keywords"` is requested. Keyword mode combines
space-separated terms with AND, recognizes a small set of common Chinese/English
synonyms, and ranks exact action names or aliases first.
```json
{
"query": "群 禁言",
"match": "keywords",
"read_only": false,
"stream": false,
"limit": 10,
"offset": 0
}
```
Both `list_actions` and `search_actions` accept optional `category`, `read_only`,
`stream`, `limit` (1–200), and `offset` (non-negative integer). Omitting pagination
returns the complete matching list and preserves `{count, actions}`. With `limit`
or `offset`, `count` is the returned page size, `total` is the matching count, and
`next_offset` is the next page offset or `null`. Pagination applies to the bundled
catalog; it does not issue server requests.
## Runtime overview
`get_runtime_info({})` returns:
- The effective `mode`, `execution_enabled`, and `write_enabled`.
- The bundled catalog version and action count. `server_support` remains
`unknown`; a matching version alone does not prove action support.
- In execution mode, effective request timeout, file size and directory quota
limits, concurrent stream limit, download directory, upload root, and whether
automatic upload is enabled. In docs mode, `transport` is `null`.
- `connection_checked: false`. Local configuration is not proof of a live login.
The overview does not expose the endpoint, access token, or authorization headers.
Configured file paths are paths on the MCP host. All stream actions still require
write mode; `upload_enabled` describes the configured file root, not permission
to bypass that gate.
`get_runtime_info({"check_connection": true})` is available only in read/write
mode with a client. It explicitly queries `get_login_info`, `get_status`, and
`get_version_info`, using the ordinary read-only execution gate. Each entry under
`checks` contains its original response and diagnostics. Partial failures remain
visible. Check the returned status data for online state; a successful HTTP
exchange alone does not establish that the account is online.
## Cancellation and progress
MCP cancellation now reaches ordinary HTTP calls as well as stream actions,
including response-body reads. Ordinary calls retain their request timeout;
streams retain separate connection and per-read idle timeouts. A cancelled call
does not automatically retry. Cancellation or timeout can leave a remote write's
outcome unknown: stopping the wait does not undo an operation already received
by SnowLuma.
When a caller includes `_meta.progressToken`, stream execution emits standard
`notifications/progress` notifications with the same token. File downloads report
decoded bytes written; automatic uploads report bytes acknowledged by the server.
Generic streams report accepted frame counts. A total is included only when a
positive file size is known. Empty files and unknown-size downloads omit it.
Notifications are limited to one per 250 ms, plus the final pending update before
a successful response. Values increase, notifications stop on cancellation or
completion, and callers without a progress token receive none. Notification
delivery failures are surfaced. Progress describes transferred data, not final
commit: only a successful tool result confirms file validation and completion.
The existing file containment, quotas, partial-file cleanup and upload-reset
behavior are unchanged. File bytes never appear in progress messages.