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
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:
commit
fa9c73363d
1367 files changed
+355735
No files matched your search
@@ -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.
|
||||
Reference in new issue
Block a user