Skip to content

feat!: scylla-mcp speaks MCP revision 2026-07-28 (stateless) - #50

Merged
CryptoJones merged 1 commit into
mainfrom
feat/mcp-2026-07-28
Aug 1, 2026
Merged

feat!: scylla-mcp speaks MCP revision 2026-07-28 (stateless)#50
CryptoJones merged 1 commit into
mainfrom
feat/mcp-2026-07-28

Conversation

@CryptoJones

Copy link
Copy Markdown
Owner

What

Brings the scylla-mcp head to MCP revision 2026-07-28 — the stateless revision.

scylla-mcp is a hand-rolled JSON-RPC server (no MCP SDK — the Rust SDK's 2026-07-28 support is still beta anyway, and this crate never used it), so the revision is implemented directly in the dispatch:

  • server/discover (a MUST in 2026-07-28): advertises supportedVersions: ["2026-07-28"], with serverInfo in the result _meta (it moved out of the body this revision). This is how a client selects the protocol now that the initialize handshake is gone.
  • resultType: "complete" on every result; tools/list also carries the CacheableResult envelope (ttlMs + cacheScope).
  • ping — removed in this revision — is now rejected (-32601), alongside logging/setLevel and resources/subscribe.
  • initialize is retained (now advertising 2025-06-18) so pre-2026 clients still connect; a 2026 client discovers first and never reaches it.

Adds a conformance.toml (probing the read-only list_functions/search against the bundled mathlib.scylla) and Rust unit tests for discover, the result envelope, and the removed methods.

Verification

Gate Result
cargo test -p scylla-mcp 16 unit + 4 integration passed
cargo test --workspace --locked all green
cargo clippy --workspace --all-targets -- -D warnings clean
mcp-conformance 0.2.0 17 passed, 1 skipped

The one skip is intentional: the head wraps binary-derived output in an <untrusted-data> envelope (DD-035) rather than flagging injection in its own inputs, so expect.injection is unset.

(No cargo fmt run — this repo's CI gates on clippy + test, not fmt --check, and main is not fmt-clean; the diff is only the logical protocol change.)

Note

This branch was cut from github/main. The working checkout had a separate fix/review-backlog-3-9 branch active; this change is independent of it.

The scylla-mcp head is a hand-rolled JSON-RPC MCP server (no SDK), so the
2026-07-28 revision is implemented directly in the dispatch:

- server/discover (a MUST in 2026-07-28): advertises supportedVersions
  ["2026-07-28"], with serverInfo in the result _meta (it moved out of the
  body this revision). This is how a client selects the version now that
  the initialize handshake is gone.
- Every result carries resultType: "complete"; tools/list also carries the
  CacheableResult envelope (ttlMs + cacheScope).
- ping — removed in 2026-07-28 — is now rejected (-32601), alongside
  logging/setLevel and resources/subscribe (which already fell through to
  the not-found arm).
- initialize is retained (advertising 2025-06-18) so pre-2026 clients still
  connect; a 2026 client discovers first and never reaches it.

Adds conformance.toml (probing the read-only list_functions/search against
the bundled mathlib.scylla) and Rust unit tests for discover, the result
envelope, and the removed methods.

scylla-mcp: 16 unit + 4 integration tests pass. Workspace: clippy -D
warnings clean, cargo test --workspace --locked all green. mcp-conformance
0.2.0 reports 17 passed, 1 skipped (injection: the head WRAPS binary-derived
output as untrusted rather than flagging its own inputs, DD-035).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added support for MCP revision 2026-07-28.
    • Added server discovery and completion metadata for tool results.
    • Added cache metadata to tool listings.
  • Compatibility
    • Legacy clients can continue using the existing initialize handshake.
  • Bug Fixes
    • Unsupported and removed protocol requests are now rejected consistently.
  • Documentation
    • Updated the unreleased changelog with MCP support details and conformance verification.

Walkthrough

Scylla MCP now supports MCP revision 2026-07-28 through server/discover, completion and cache metadata, and resultType envelopes. Legacy initialize remains on 2025-06-18. Removed RPCs are rejected, with conformance configuration and tests added.

Changes

MCP protocol update

Layer / File(s) Summary
Protocol discovery and capability contracts
crates/scylla-mcp/src/lib.rs
The server advertises the 2026-07-28 revision through server/discover. Legacy initialize continues to advertise 2025-06-18.
Tool response metadata and rejection behavior
crates/scylla-mcp/src/lib.rs
tools/list and tools/call include completion metadata. tools/list includes cache metadata. Removed methods are rejected, and tests cover these responses.
Conformance configuration and release record
conformance.toml, CHANGELOG.md
Conformance runs read-only probes against the MCP binary and Mathlib fixture. The changelog records the protocol support and verification.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MCPConformance
  participant ScyllaMCP
  participant MathlibFixture
  MCPConformance->>ScyllaMCP: server/discover
  ScyllaMCP-->>MCPConformance: protocol and capability metadata
  MCPConformance->>ScyllaMCP: tools/list
  ScyllaMCP-->>MCPConformance: tools with completion and cache metadata
  MCPConformance->>ScyllaMCP: tools/call search
  ScyllaMCP->>MathlibFixture: read-only search
  MathlibFixture-->>ScyllaMCP: search output
  ScyllaMCP-->>MCPConformance: resultType complete response
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: updating scylla-mcp to MCP revision 2026-07-28 with stateless behavior.
Description check ✅ Passed The description directly explains the MCP revision update, protocol changes, tests, conformance results, and compatibility behavior.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mcp-2026-07-28

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CHANGELOG.md`:
- Around line 16-18: Update the changelog sentence around the resultType
description to remove the claim that every result includes it, and specify that
resultType applies only to the 2026 discovery, tool-list, and tool-call response
envelopes while preserving the existing initialize behavior.

In `@conformance.toml`:
- Around line 10-11: Update the scylla-mcp command’s path-handling and dispatch
flow so SCYLLA_MCP_ROOT is read and enforced as the base directory for all
client-supplied paths, including the std::fs::read calls used by diff and merge
and the std::fs::write call used by export. Resolve or validate each path
against the configured root before performing filesystem access, preserving "."
as the configured root for this conformance command.

In `@crates/scylla-mcp/src/lib.rs`:
- Around line 454-484: Expand discover_advertises_the_2026_revision to assert
the exact supportedVersions list, ttlMs, cacheScope, and
_meta["io.modelcontextprotocol/serverInfo"], while confirming serverInfo is
absent from the result body. Add a tools/call error-path assertion that still
requires resultType to equal "complete", using the existing dispatch/session
test patterns.
- Around line 226-232: Update the legacy "initialize" handling in the request
dispatcher to associate sessions advertising protocolVersion "2025-06-18" with a
handler that returns the MCP-required empty response for "ping"; otherwise
remove this compatibility response rather than advertising unsupported behavior.
Add a regression test covering initialize followed by ping and asserting the
successful empty ping result.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2863ca3d-2f86-4f5b-bd2a-700e68730ebf

📥 Commits

Reviewing files that changed from the base of the PR and between 6fb100f and 99cd4ef.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • conformance.toml
  • crates/scylla-mcp/src/lib.rs
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: engine-service
  • GitHub Check: test
🔇 Additional comments (2)
crates/scylla-mcp/src/lib.rs (2)

25-27: LGTM!


254-260: LGTM!

Comment thread CHANGELOG.md
Comment on lines +16 to +18
(advertising `2026-07-28`, with `serverInfo` in the result `_meta`), stamps
every result with `resultType`, and adds the `CacheableResult` envelope
(`ttlMs` + `cacheScope`) to `tools/list`. `ping` — removed in this revision —

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Limit the resultType claim to 2026 response envelopes.

initialize intentionally returns a 2025-06-18 result without resultType. Replace “every result” with language that identifies the 2026 discovery, tool-list, and tool-call responses.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CHANGELOG.md` around lines 16 - 18, Update the changelog sentence around the
resultType description to remove the claim that every result includes it, and
specify that resultType applies only to the 2026 discovery, tool-list, and
tool-call response envelopes while preserving the existing initialize behavior.

Comment thread conformance.toml
Comment on lines +10 to +11
command = ["target/debug/scylla-mcp", "crates/scylla-wasm/web/mathlib.scylla"]
env = { SCYLLA_MCP_ROOT = "." }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 5 'SCYLLA_MCP_ROOT' crates
rg -n -C 4 'std::fs::(read|write)' crates/scylla-mcp

Repository: CryptoJones/Scylla

Length of output: 156


🏁 Script executed:

set -euo pipefail

printf '%s\n' '--- tracked files ---'
git ls-files | sed -n '1,160p'

printf '%s\n' '--- conformance.toml ---'
cat -n conformance.toml

printf '%s\n' '--- root-variable references ---'
rg -n -i -C 4 'SCYLLA_MCP_ROOT|dispatch|diff|export|merge' . --glob '!target/**' --glob '!node_modules/**' | sed -n '1,260p'

printf '%s\n' '--- filesystem calls ---'
rg -n -C 3 'std::fs|fs::|read_to_string|write_all|File::|canonicalize|PathBuf|Path::' crates --glob '*.rs' | sed -n '1,260p'

Repository: CryptoJones/Scylla

Length of output: 48451


🏁 Script executed:

set -euo pipefail

printf '%s\n' '--- MCP source outline ---'
ast-grep outline crates/scylla-mcp/src/lib.rs
ast-grep outline crates/scylla-mcp/src/main.rs

printf '%s\n' '--- MCP main ---'
cat -n crates/scylla-mcp/src/main.rs

printf '%s\n' '--- MCP dispatch and filesystem tools ---'
cat -n crates/scylla-mcp/src/lib.rs | sed -n '1,235p'

printf '%s\n' '--- MCP tests around filesystem tools ---'
cat -n crates/scylla-mcp/src/lib.rs | sed -n '350,435p'
cat -n crates/scylla-mcp/tests/conformance.rs | sed -n '1,180p'

Repository: CryptoJones/Scylla

Length of output: 31104


Enforce SCYLLA_MCP_ROOT before dispatch

scylla-mcp does not read SCYLLA_MCP_ROOT. diff and merge call std::fs::read, and export calls std::fs::write with client-supplied paths. The configuration does not confine these tools to ".".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@conformance.toml` around lines 10 - 11, Update the scylla-mcp command’s
path-handling and dispatch flow so SCYLLA_MCP_ROOT is read and enforced as the
base directory for all client-supplied paths, including the std::fs::read calls
used by diff and merge and the std::fs::write call used by export. Resolve or
validate each path against the configured root before performing filesystem
access, preserving "." as the configured root for this conformance command.

Comment on lines +226 to 232
// Retained for pre-2026 clients that still open with the handshake. The
// 2026-07-28 client never reaches this — it discovers first.
"initialize" => json!({"jsonrpc": "2.0", "id": id, "result": {
"protocolVersion": "2024-11-05",
"protocolVersion": "2025-06-18",
"serverInfo": {"name": "scylla-mcp", "version": env!("CARGO_PKG_VERSION")},
"capabilities": {"tools": {}}
}}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- candidate file map ---'
ast-grep outline crates/scylla-mcp/src/lib.rs --lang rust || true
printf '%s\n' '--- relevant source ---'
sed -n '150,280p' crates/scylla-mcp/src/lib.rs
printf '%s\n' '--- references to protocol dispatch and ping ---'
rg -n -C 3 'initialize|ping|protocolVersion|jsonrpc|32601|2025-06-18' crates/scylla-mcp
printf '%s\n' '--- tests ---'
rg -n -C 4 '#\[test\]|#\[tokio::test\]|initialize|ping' crates/scylla-mcp --glob '*.rs'

Repository: CryptoJones/Scylla

Length of output: 44283


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- imports, protocol constants, and session type ---'
sed -n '1,120p' crates/scylla-mcp/src/lib.rs
printf '%s\n' '--- exact dispatch tests and helper context ---'
sed -n '440,500p' crates/scylla-mcp/src/lib.rs
printf '%s\n' '--- session declarations and protocol-related fields ---'
rg -n -C 4 'struct Session|pub struct Session|protocol|version|dispatch\(' crates/scylla-mcp crates --glob '*.rs'
printf '%s\n' '--- deterministic source-level behavior probe ---'
python3 - <<'PY'
from pathlib import Path
p = Path("crates/scylla-mcp/src/lib.rs").read_text()
dispatch = p[p.index("pub fn dispatch"):p.index("#[cfg(test)]", p.index("pub fn dispatch"))]
init = dispatch[dispatch.index('"initialize"'):dispatch.index('// List results', dispatch.index('"initialize"'))]
ping = '"ping" in dispatch or '"ping/' in dispatch
default = 'code": -32601' in dispatch and 'method not found' in dispatch
print("initialize advertises 2025-06-18:", '"protocolVersion": "2025-06-18"' in init)
print("initialize arm stores protocol state:", any(x in init for x in ("session.", "protocol", "selected")))
print("dispatch has a ping arm:", ping)
print("unknown methods return -32601:", default)
assert '"protocolVersion": "2025-06-18"' in init
assert not any(x in init for x in ("session.", "protocol", "selected"))
assert not ping
assert default
PY

Repository: CryptoJones/Scylla

Length of output: 50374


🌐 Web query:

site:modelcontextprotocol.io/specification/2025-06-18/basic/utilities/ping MCP 2025-06-18 ping receiver MUST respond

💡 Result:

According to the Model Context Protocol (MCP) specification dated June 18, 2025, the ping mechanism is a utility used by either the client or the server to verify that the connection is active and the counterpart is responsive [1]. Per the specification's behavior requirements, the receiver of a ping request MUST respond promptly with an empty JSON-RPC response [1]. The required response format is as follows: { "jsonrpc": "2.0", "id": "123", "result": {} } If the sender does not receive a response within a reasonable timeout, it may consider the connection stale, terminate the connection, or attempt to reconnect [1]. Additionally, the specification suggests that implementations should periodically issue pings, make ping frequency configurable, and avoid excessive pinging to minimize network overhead [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- focused dispatch source ---'
sed -n '200,270p' crates/scylla-mcp/src/lib.rs
printf '%s\n' '--- MCP main loop ---'
sed -n '1,75p' crates/scylla-mcp/src/main.rs
printf '%s\n' '--- deterministic dispatch-shape probe ---'
python3 - <<'PY'
from pathlib import Path
src = Path("crates/scylla-mcp/src/lib.rs").read_text()
start = src.index("pub fn dispatch")
end = src.index("#[cfg(test)]", start)
body = src[start:end]
init_start = body.index('"initialize"')
init_end = body.index('// List results', init_start)
init_arm = body[init_start:init_end]
print("initialize advertises:", "2025-06-18" if '"protocolVersion": "2025-06-18"' in init_arm else "none")
print("initialize mutates session:", any(token in init_arm for token in ("session.", "session,", "selected_protocol")))
print("explicit ping arm:", '"ping" =>' in body)
print("default -32601 branch:", '"error": {"code": -32601' in body and 'method not found' in body)
print("same dispatch function handles initialize and ping:", '"initialize"' in body and '"ping" =>' not in body)
assert '"protocolVersion": "2025-06-18"' in init_arm
assert not any(token in init_arm for token in ("session.", "session,", "selected_protocol"))
assert '"ping" =>' not in body
assert '"error": {"code": -32601' in body and 'method not found' in body
PY

Repository: CryptoJones/Scylla

Length of output: 6075


Preserve the advertised legacy protocol behavior.

If initialize advertises 2025-06-18, route that session to a handler that supports ping; otherwise remove the legacy compatibility response. ping currently falls through to -32601, although MCP 2025-06-18 requires an empty response. Add a regression test for initialize followed by ping.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/scylla-mcp/src/lib.rs` around lines 226 - 232, Update the legacy
"initialize" handling in the request dispatcher to associate sessions
advertising protocolVersion "2025-06-18" with a handler that returns the
MCP-required empty response for "ping"; otherwise remove this compatibility
response rather than advertising unsupported behavior. Add a regression test
covering initialize followed by ping and asserting the successful empty ping
result.

Comment on lines +454 to +484
#[test]
fn discover_advertises_the_2026_revision() {
// MCP 2026-07-28: server/discover is a MUST and is how a client selects a
// version without the (removed) initialize handshake.
let mut s = session();
let resp = dispatch(&mut s, &json!({"jsonrpc": "2.0", "id": 1, "method": "server/discover"}));
let versions = resp["result"]["supportedVersions"].as_array().unwrap();
assert!(
versions.iter().any(|v| v == "2026-07-28"),
"discover must advertise 2026-07-28, got {versions:?}"
);
assert_eq!(resp["result"]["resultType"], "complete");
}

#[test]
fn list_result_carries_the_cacheable_envelope() {
// Every result carries resultType; list results also carry ttlMs + cacheScope.
let mut s = session();
let resp = dispatch(&mut s, &json!({"jsonrpc": "2.0", "id": 1, "method": "tools/list"}));
assert_eq!(resp["result"]["resultType"], "complete");
assert!(resp["result"]["ttlMs"].is_number(), "tools/list must carry ttlMs");
assert_eq!(resp["result"]["cacheScope"], "public");
}

#[test]
fn call_result_carries_result_type() {
let mut s = session();
let resp = dispatch(&mut s, &json!({"jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": {"name": "list_functions", "arguments": {}}}));
assert_eq!(resp["result"]["resultType"], "complete");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the complete discovery contract.

The test only checks that supportedVersions contains the new revision. Assert the exact version list, ttlMs, cacheScope, and _meta["io.modelcontextprotocol/serverInfo"]. Also assert that serverInfo is absent from the discovery result body.

Add an error-path tools/call assertion for resultType: "complete".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/scylla-mcp/src/lib.rs` around lines 454 - 484, Expand
discover_advertises_the_2026_revision to assert the exact supportedVersions
list, ttlMs, cacheScope, and _meta["io.modelcontextprotocol/serverInfo"], while
confirming serverInfo is absent from the result body. Add a tools/call
error-path assertion that still requires resultType to equal "complete", using
the existing dispatch/session test patterns.

@CryptoJones
CryptoJones merged commit 362bc42 into main Aug 1, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant