diff --git a/.config/jp/tools/src/bash_tests.rs b/.config/jp/tools/src/bash_tests.rs
index 9f1fa2799..c156d1e34 100644
--- a/.config/jp/tools/src/bash_tests.rs
+++ b/.config/jp/tools/src/bash_tests.rs
@@ -563,78 +563,3 @@ fn a_successful_run_still_reports_its_exit_code() {
0\n\n```"
);
}
-
-/// The install script's whole point: a base image without python gets it, and
-/// the built image is what the commands then run in.
-///
-/// Also covers the `USER` round-trip — the script installs as root, and
-/// `python3 -c` runs as `nonroot` afterwards, which only works if the restore
-/// resolved to a user the base image actually defines.
-///
-/// Needs a container runtime and network access for the package install.
-/// The first run builds; later runs hit the cached tag and take about as long
-/// as any other call.
-#[test]
-#[ignore = "builds a real image"]
-fn an_install_script_adds_a_tool_the_base_image_lacks() {
- let dir = workspace();
- let plan = Plan {
- image: DEFAULT_IMAGE.to_owned(),
- install: Some(Install {
- script: "apk add --no-cache python3".to_owned(),
- run_as: DEFAULT_RUN_AS.to_owned(),
- }),
- mounts: vec![],
- envs: vec![],
- script: "set -euo pipefail\npython3 -c 'print(1 + 1)'\nid -un\n".to_owned(),
- };
- let runtime = detect().expect("a container runtime must be installed");
-
- let content = execute(dir.path(), runtime, &plan, &DuctProcessRunner)
- .unwrap()
- .unwrap_content();
-
- assert_eq!(
- content,
- "```xml\n\n 2\nnonroot\n \
- 0\n\n```"
- );
-}
-
-/// Read-only mounts are what keep this tool from being able to replace the
-/// workspace-editing tools, so it is worth proving against a real runtime: a
-/// runtime that ignored `:ro` would drop the guarantee silently.
-///
-/// Runs against [`DEFAULT_IMAGE`], so it also covers the two things about an
-/// image that this tool depends on: that `bash` is present, and that no
-/// `ENTRYPOINT` swallows the `bash -c` invocation.
-///
-/// Needs a container runtime that is allowed to share the temp directory
-/// (Docker Desktop restricts which host paths it will bind-mount).
-#[test]
-#[ignore = "starts a real container"]
-fn a_mounted_path_is_read_only_in_the_container() {
- let dir = workspace();
- let plan = Plan {
- image: DEFAULT_IMAGE.to_owned(),
- install: None,
- mounts: vec![(
- dir.path().join("crates").canonicalize_utf8().unwrap(),
- "/workspace/crates".to_owned(),
- )],
- envs: vec![],
- script: "set -euo pipefail\necho written > /workspace/crates/lib.rs\n".to_owned(),
- };
- let runtime = detect().expect("a container runtime must be installed");
-
- let content = execute(dir.path(), runtime, &plan, &DuctProcessRunner)
- .unwrap()
- .unwrap_content();
-
- // Under `set -e` bash exits 1 when the redirect cannot open the file.
- assert!(content.contains("1"), "got: {content}");
- assert_eq!(
- std::fs::read_to_string(dir.path().join("crates/lib.rs")).unwrap(),
- ""
- );
-}
diff --git a/.config/supply-chain/audits.toml b/.config/supply-chain/audits.toml
index 1cf4ca9fa..7a7607696 100644
--- a/.config/supply-chain/audits.toml
+++ b/.config/supply-chain/audits.toml
@@ -26,6 +26,11 @@ who = "Jean Mertz "
criteria = "safe-to-deploy"
version = "0.7.3"
+[[audits.chacha20]]
+who = "Jean Mertz "
+criteria = "safe-to-deploy"
+delta = "0.10.0 -> 0.10.2"
+
[[audits.comfy-table]]
who = "Jean Mertz "
criteria = "safe-to-deploy"
@@ -46,6 +51,11 @@ who = "Jean Mertz "
criteria = "safe-to-deploy"
delta = "0.2.14 -> 0.3.5"
+[[audits.cpufeatures]]
+who = "Jean Mertz "
+criteria = "safe-to-deploy"
+delta = "0.3.0 -> 0.3.1"
+
[[audits.datetime_literal]]
who = "Jean Mertz "
criteria = "safe-to-deploy"
@@ -81,6 +91,11 @@ who = "Jean Mertz "
criteria = "safe-to-deploy"
version = "0.1.5"
+[[audits.getrandom]]
+who = "Jean Mertz "
+criteria = "safe-to-deploy"
+delta = "0.4.2 -> 0.4.3"
+
[[audits.hashlink]]
who = "Jean Mertz "
criteria = "safe-to-deploy"
@@ -196,6 +211,11 @@ who = "Jean Mertz "
criteria = "safe-to-deploy"
delta = "0.39.2 -> 0.41.0"
+[[audits.r-efi]]
+who = "Jean Mertz "
+criteria = "safe-to-deploy"
+delta = "5.3.0 -> 6.0.0"
+
[[audits.ra-ap-rustc_lexer]]
who = "Jean Mertz "
criteria = "safe-to-deploy"
@@ -206,6 +226,16 @@ who = "Jean Mertz "
criteria = "safe-to-deploy"
delta = "0.9.4 -> 0.9.5"
+[[audits.rand]]
+who = "Jean Mertz "
+criteria = "safe-to-deploy"
+delta = "0.10.1 -> 0.10.2"
+
+[[audits.rand_core]]
+who = "Jean Mertz "
+criteria = "safe-to-deploy"
+delta = "0.10.0 -> 0.10.1"
+
[[audits.rand_xorshift]]
who = "Jean Mertz "
criteria = "safe-to-deploy"
@@ -261,6 +291,11 @@ who = "Jean Mertz "
criteria = "safe-to-deploy"
delta = "0.23.35 -> 0.23.37"
+[[audits.rustls]]
+who = "Jean Mertz "
+criteria = "safe-to-deploy"
+delta = "0.23.35 -> 0.23.45"
+
[[audits.rustls-webpki]]
who = "Jean Mertz "
criteria = "safe-to-deploy"
@@ -271,6 +306,11 @@ who = "Jean Mertz "
criteria = "safe-to-deploy"
delta = "0.103.10 -> 0.103.12"
+[[audits.rustls-webpki]]
+who = "Jean Mertz "
+criteria = "safe-to-deploy"
+delta = "0.103.13 -> 0.103.15"
+
[[audits.ruzstd]]
who = "Jean Mertz "
criteria = "safe-to-deploy"
@@ -321,6 +361,11 @@ who = "Jean Mertz "
criteria = "safe-to-deploy"
version = "0.5.2"
+[[audits.sse-stream]]
+who = "Jean Mertz "
+criteria = "safe-to-deploy"
+version = "0.2.6"
+
[[audits.string_cache]]
who = "Jean Mertz "
criteria = "safe-to-deploy"
@@ -533,6 +578,12 @@ trusted-publisher = "github:rust-lang/cc-rs"
start = "2025-09-01"
end = "2027-03-04"
+[[trusted.chacha20]]
+criteria = "safe-to-deploy"
+trusted-publisher = "github:RustCrypto/stream-ciphers"
+start = "2026-02-06"
+end = "2027-09-23"
+
[[trusted.clap]]
criteria = "safe-to-deploy"
user-id = 6743 # Ed Page (epage)
@@ -575,6 +626,12 @@ user-id = 696 # Nick Fitzgerald (fitzgen)
start = "2019-07-30"
end = "2027-08-19"
+[[trusted.cpufeatures]]
+criteria = "safe-to-deploy"
+user-id = 267 # Tony Arcieri (tarcieri)
+start = "2021-04-26"
+end = "2027-09-23"
+
[[trusted.dtoa]]
criteria = "safe-to-deploy"
user-id = 3618 # David Tolnay (dtolnay)
@@ -923,6 +980,18 @@ user-id = 3618 # David Tolnay (dtolnay)
start = "2019-04-09"
end = "2027-02-13"
+[[trusted.rand]]
+criteria = "safe-to-deploy"
+trusted-publisher = "github:rust-random/rand"
+start = "2026-01-26"
+end = "2027-09-23"
+
+[[trusted.rand_core]]
+criteria = "safe-to-deploy"
+trusted-publisher = "github:rust-random/rand_core"
+start = "2026-01-20"
+end = "2027-09-23"
+
[[trusted.ref-cast]]
criteria = "safe-to-deploy"
user-id = 3618 # David Tolnay (dtolnay)
diff --git a/.config/supply-chain/config.toml b/.config/supply-chain/config.toml
index e7b654e1e..30e3ecc37 100644
--- a/.config/supply-chain/config.toml
+++ b/.config/supply-chain/config.toml
@@ -153,10 +153,6 @@ criteria = "safe-to-deploy"
version = "0.10.1"
criteria = "safe-to-deploy"
-[[exemptions.cpufeatures]]
-version = "0.2.17"
-criteria = "safe-to-deploy"
-
[[exemptions.crc32fast]]
version = "1.5.0"
criteria = "safe-to-deploy"
diff --git a/.config/supply-chain/imports.lock b/.config/supply-chain/imports.lock
index 07d124a3b..f1457de64 100644
--- a/.config/supply-chain/imports.lock
+++ b/.config/supply-chain/imports.lock
@@ -130,6 +130,11 @@ version = "1.2.56"
when = "2026-02-13"
trusted-publisher = "github:rust-lang/cc-rs"
+[[publisher.chacha20]]
+version = "0.10.2"
+when = "2026-08-27"
+trusted-publisher = "github:RustCrypto/stream-ciphers"
+
[[publisher.clap]]
version = "4.5.48"
when = "2025-09-19"
@@ -179,6 +184,20 @@ user-id = 696
user-login = "fitzgen"
user-name = "Nick Fitzgerald"
+[[publisher.cpufeatures]]
+version = "0.2.17"
+when = "2025-01-25"
+user-id = 267
+user-login = "tarcieri"
+user-name = "Tony Arcieri"
+
+[[publisher.cpufeatures]]
+version = "0.3.1"
+when = "2026-08-26"
+user-id = 267
+user-login = "tarcieri"
+user-name = "Tony Arcieri"
+
[[publisher.dtoa]]
version = "1.0.11"
when = "2025-12-27"
@@ -552,6 +571,16 @@ user-id = 3618
user-login = "dtolnay"
user-name = "David Tolnay"
+[[publisher.rand]]
+version = "0.10.2"
+when = "2026-07-02"
+trusted-publisher = "github:rust-random/rand"
+
+[[publisher.rand_core]]
+version = "0.10.1"
+when = "2026-04-13"
+trusted-publisher = "github:rust-random/rand_core"
+
[[publisher.ref-cast]]
version = "1.0.24"
when = "2025-03-03"
@@ -651,8 +680,8 @@ user-login = "dtolnay"
user-name = "David Tolnay"
[[publisher.serde_json]]
-version = "1.0.149"
-when = "2026-01-06"
+version = "1.0.151"
+when = "2026-07-20"
user-id = 3618
user-login = "dtolnay"
user-name = "David Tolnay"
@@ -1425,6 +1454,12 @@ who = "Pat Hickey "
criteria = "safe-to-deploy"
delta = "0.3.28 -> 0.3.31"
+[[audits.bytecode-alliance.audits.getrandom]]
+who = "Alex Crichton "
+criteria = "safe-to-deploy"
+delta = "0.4.1 -> 0.4.2"
+notes = "Nothing awry in this update, standard updates for some platforms and other misc things."
+
[[audits.bytecode-alliance.audits.gimli]]
who = "Alex Crichton "
criteria = "safe-to-deploy"
@@ -1599,28 +1634,12 @@ who = "Alex Crichton "
criteria = "safe-to-deploy"
delta = "0.1.21 -> 0.1.24"
-[[audits.bytecode-alliance.audits.rustls]]
-who = "Alex Crichton "
-criteria = "safe-to-deploy"
-delta = "0.23.37 -> 0.23.45"
-notes = """
-A relatively large update, but no new `unsafe` and nothing awry here. Lots of
-protocol/etc updates which I'm not personally an expert within but the rustls
-maintainers are relatively well trusted as well.
-"""
-
[[audits.bytecode-alliance.audits.rustls-webpki]]
who = "Alex Crichton "
criteria = "safe-to-deploy"
delta = "0.103.12 -> 0.103.13"
notes = "Minor fixes for the bug being fixed in this release, nothing awry."
-[[audits.bytecode-alliance.audits.rustls-webpki]]
-who = "Alex Crichton "
-criteria = "safe-to-deploy"
-delta = "0.103.13 -> 0.103.15"
-notes = "Minor updates and feature shufflings."
-
[[audits.bytecode-alliance.audits.sha1]]
who = "Andrew Brown "
criteria = "safe-to-deploy"
@@ -2732,6 +2751,16 @@ who = "J.C. Jones "
criteria = "safe-to-deploy"
delta = "1.0.1 -> 1.0.3"
+[[audits.isrg.audits.getrandom]]
+who = "David Cook "
+criteria = "safe-to-deploy"
+delta = "0.3.4 -> 0.4.0"
+
+[[audits.isrg.audits.getrandom]]
+who = "David Cook "
+criteria = "safe-to-deploy"
+delta = "0.4.0 -> 0.4.1"
+
[[audits.isrg.audits.libbz2-rs-sys]]
who = "Ameer Ghani "
criteria = "safe-to-deploy"
diff --git a/Cargo.lock b/Cargo.lock
index 83292fbdd..ca4ed6bb6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -564,6 +564,17 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+[[package]]
+name = "chacha20"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06"
+dependencies = [
+ "cfg-if",
+ "cpufeatures 0.3.1",
+ "rand_core 0.10.1",
+]
+
[[package]]
name = "chrono"
version = "0.4.42"
@@ -772,6 +783,15 @@ dependencies = [
"libc",
]
+[[package]]
+name = "cpufeatures"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "crc32fast"
version = "1.5.0"
@@ -1490,11 +1510,23 @@ dependencies = [
"cfg-if",
"js-sys",
"libc",
- "r-efi",
+ "r-efi 5.3.0",
"wasip2",
"wasm-bindgen",
]
+[[package]]
+name = "getrandom"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi 6.0.0",
+ "rand_core 0.10.1",
+]
+
[[package]]
name = "gimli"
version = "0.31.1"
@@ -2146,7 +2178,6 @@ dependencies = [
"camino",
"dyn-clone",
"dyn-hash",
- "jp_mcp",
"linkme",
"percent-encoding",
"serde",
@@ -2163,7 +2194,6 @@ dependencies = [
"duct",
"indoc",
"jp_attachment",
- "jp_mcp",
"serde",
"serde_json",
"tokio",
@@ -2180,7 +2210,6 @@ dependencies = [
"grizzly",
"indoc",
"jp_attachment",
- "jp_mcp",
"quick-xml",
"serde",
"test-log",
@@ -2196,10 +2225,8 @@ dependencies = [
"camino",
"camino-tempfile",
"duct",
- "indexmap",
"indoc",
"jp_attachment",
- "jp_mcp",
"quick-xml",
"serde",
"shlex",
@@ -2218,10 +2245,8 @@ dependencies = [
"crossbeam-channel",
"glob",
"ignore",
- "indexmap",
"infer",
"jp_attachment",
- "jp_mcp",
"serde",
"test-log",
"tokio",
@@ -2238,7 +2263,6 @@ dependencies = [
"glob",
"jp_attachment",
"jp_github",
- "jp_mcp",
"serde",
"tracing",
"url",
@@ -2252,7 +2276,6 @@ dependencies = [
"camino",
"htmd",
"jp_attachment",
- "jp_mcp",
"reqwest",
"serde",
"tracing",
@@ -2285,9 +2308,8 @@ dependencies = [
"async-trait",
"camino",
"jp_attachment",
- "jp_mcp",
- "quick-xml",
"serde",
+ "tokio",
"url",
]
@@ -2348,10 +2370,12 @@ dependencies = [
"minijinja",
"pretty_assertions",
"quick-xml",
+ "rand 0.9.5",
"rayon",
"regex",
"relative-path",
"reqwest",
+ "rmcp",
"schemars",
"schematic",
"serde",
@@ -2539,6 +2563,7 @@ dependencies = [
"async-trait",
"base64",
"camino",
+ "camino-tempfile",
"chrono",
"datetime_literal",
"eventsource-stream",
@@ -2553,17 +2578,16 @@ dependencies = [
"jp_config",
"jp_conversation",
"jp_credentials",
- "jp_mcp",
"jp_openrouter",
"jp_storage",
"jp_test",
"jp_tool",
- "minijinja",
"ollama-rs",
"openai_responses",
"paste",
+ "process-wrap",
"quick-xml",
- "rand",
+ "rand 0.9.5",
"reqwest",
"reqwest-eventsource",
"saphyr",
@@ -2573,8 +2597,8 @@ dependencies = [
"test-log",
"thiserror 2.0.20",
"tokio",
- "tokio-util",
"tracing",
+ "tracing-subscriber",
"url",
"uuid",
]
@@ -2587,16 +2611,29 @@ version = "0.1.0"
name = "jp_mcp"
version = "0.1.0"
dependencies = [
+ "assert_matches",
+ "async-trait",
+ "axum",
+ "base64",
+ "camino",
+ "camino-tempfile",
+ "futures",
"indexmap",
"jp_config",
+ "jp_tool",
+ "minijinja",
+ "reqwest",
"rmcp",
"serde",
"serde_json",
"sha1",
"sha2",
+ "sse-stream",
"thiserror 2.0.20",
"tokio",
+ "tokio-util",
"tracing",
+ "url",
"which",
]
@@ -2747,6 +2784,7 @@ version = "0.1.0"
dependencies = [
"camino",
"camino-tempfile",
+ "indexmap",
"serde",
"serde_json",
"thiserror 2.0.20",
@@ -3498,7 +3536,7 @@ checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40"
dependencies = [
"bitflags 2.9.4",
"num-traits",
- "rand",
+ "rand 0.9.5",
"rand_chacha",
"rand_xorshift",
"regex-syntax",
@@ -3545,7 +3583,7 @@ dependencies = [
"bytes",
"getrandom 0.3.4",
"lru-slab",
- "rand",
+ "rand 0.9.5",
"ring",
"rustc-hash 2.1.1",
"rustls",
@@ -3586,6 +3624,12 @@ version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+[[package]]
+name = "r-efi"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
+
[[package]]
name = "ra-ap-rustc_lexer"
version = "0.167.0"
@@ -3604,7 +3648,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41"
dependencies = [
"rand_chacha",
- "rand_core",
+ "rand_core 0.9.3",
+]
+
+[[package]]
+name = "rand"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
+dependencies = [
+ "chacha20",
+ "getrandom 0.4.3",
+ "rand_core 0.10.1",
]
[[package]]
@@ -3614,7 +3669,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
- "rand_core",
+ "rand_core 0.9.3",
]
[[package]]
@@ -3626,13 +3681,19 @@ dependencies = [
"getrandom 0.3.4",
]
+[[package]]
+name = "rand_core"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
+
[[package]]
name = "rand_xorshift"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
dependencies = [
- "rand_core",
+ "rand_core 0.9.3",
]
[[package]]
@@ -3845,20 +3906,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d1f571c72940a19d9532fe52dbea8bc9912bf1d766c2970bb824056b86f3f59"
dependencies = [
"async-trait",
+ "bytes",
"chrono",
"futures",
+ "http",
+ "http-body",
+ "http-body-util",
"pastey",
"pin-project-lite",
"process-wrap",
+ "rand 0.10.2",
"rmcp-macros",
"schemars",
"serde",
"serde_json",
+ "sse-stream",
"thiserror 2.0.20",
"tokio",
"tokio-stream",
"tokio-util",
+ "tower-service",
"tracing",
+ "uuid",
]
[[package]]
@@ -4070,7 +4139,6 @@ dependencies = [
"schemars_derive",
"serde",
"serde_json",
- "url",
]
[[package]]
@@ -4281,9 +4349,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.149"
+version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
+checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
dependencies = [
"indexmap",
"itoa",
@@ -4409,7 +4477,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
- "cpufeatures",
+ "cpufeatures 0.2.17",
"digest",
]
@@ -4426,7 +4494,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
- "cpufeatures",
+ "cpufeatures 0.2.17",
"digest",
]
@@ -4557,6 +4625,19 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "sse-stream"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c25ac7aff0abd1dbc474536e40416e1102c7dd9bfba0b9861c6d357f835dcfb4"
+dependencies = [
+ "bytes",
+ "futures-util",
+ "http-body",
+ "http-body-util",
+ "pin-project-lite",
+]
+
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
@@ -5346,7 +5427,9 @@ version = "1.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37"
dependencies = [
+ "getrandom 0.4.3",
"js-sys",
+ "serde_core",
"sha1_smol",
"wasm-bindgen",
]
diff --git a/Cargo.toml b/Cargo.toml
index 3d42c3358..e8da91626 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -114,6 +114,7 @@ percent-encoding = { version = "2", default-features = false }
portable-pty = { version = "0.9", default-features = false }
pretty_assertions = { version = "1", default-features = false }
proc-macro2 = { version = "1", default-features = false }
+process-wrap = { version = "9", default-features = false }
proptest = { version = "1", default-features = false }
quick-xml = { version = "0.41", default-features = false }
quote = { version = "1", default-features = false }
@@ -131,7 +132,7 @@ rowan = { version = "0.16", default-features = false }
rusqlite = { version = "0.38", default-features = false }
rustc-demangle = { version = "0.1", default-features = false }
saphyr = { git = "https://github.com/JeanMertz/saphyr", branch = "jean/fix-multinewline-endings", default-features = false } #
-schemars = { version = "1.0.0-alpha.17", default-features = false }
+schemars = { version = "1", default-features = false }
scraper = { version = "0.25", default-features = false }
secrecy = { version = "0.10", default-features = false }
semver = { version = "1", default-features = false }
@@ -147,6 +148,7 @@ sha1 = { version = "0.10", default-features = false }
sha2 = { version = "0.10", default-features = false }
shlex = { version = "1", default-features = false }
similar = { version = "2", default-features = false }
+sse-stream = { version = "0.2", default-features = false }
strip-ansi-escapes = { version = "0.2", default-features = false }
syn = { version = "2", default-features = false }
syntect = { version = "5.3", default-features = false }
diff --git a/crates/contrib/async-anthropic/src/types.rs b/crates/contrib/async-anthropic/src/types.rs
index 6852dc4d4..6dc0a520c 100644
--- a/crates/contrib/async-anthropic/src/types.rs
+++ b/crates/contrib/async-anthropic/src/types.rs
@@ -12,8 +12,30 @@ use crate::messages;
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
pub struct Usage {
+ /// Uncached input tokens; cache reads and writes are reported separately.
pub input_tokens: Option,
+ /// Generated output tokens.
pub output_tokens: Option,
+ /// Input tokens written to the prompt cache.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub cache_creation_input_tokens: Option,
+ /// Input tokens read from the prompt cache.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub cache_read_input_tokens: Option,
+ /// Cache writes classified by retention period, when reported.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub cache_creation: Option,
+}
+
+/// Prompt-cache writes classified by retention period.
+#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
+pub struct CacheCreationUsage {
+ /// Tokens written with a five-minute retention period.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub ephemeral_5m_input_tokens: Option,
+ /// Tokens written with a one-hour retention period.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub ephemeral_1h_input_tokens: Option,
}
#[derive(Clone, Debug, Deserialize)]
diff --git a/crates/contrib/bookworm/Cargo.toml b/crates/contrib/bookworm/Cargo.toml
index 0665e2532..18eb282f6 100644
--- a/crates/contrib/bookworm/Cargo.toml
+++ b/crates/contrib/bookworm/Cargo.toml
@@ -18,7 +18,7 @@ indoc = { workspace = true }
reqwest = { workspace = true, features = ["json", "rustls-tls"] }
rmcp = { workspace = true, features = ["server", "transport-io", "macros"] }
rusqlite = { workspace = true, features = ["bundled", "array", "vtab"] }
-schemars = { workspace = true, features = ["preserve_order", "schemars_derive", "std", "url2"] }
+schemars = { workspace = true, features = ["derive", "preserve_order", "std"] }
scraper = { workspace = true }
semver = { workspace = true }
serde = { workspace = true, features = ["derive"] }
diff --git a/crates/jp_attachment/Cargo.toml b/crates/jp_attachment/Cargo.toml
index e787c8aa4..19337aaf5 100644
--- a/crates/jp_attachment/Cargo.toml
+++ b/crates/jp_attachment/Cargo.toml
@@ -13,8 +13,6 @@ repository.workspace = true
version.workspace = true
[dependencies]
-jp_mcp = { workspace = true }
-
async-trait = { workspace = true }
camino = { workspace = true }
dyn-clone = { workspace = true }
diff --git a/crates/jp_attachment/src/lib.rs b/crates/jp_attachment/src/lib.rs
index 8f3365614..983e32b02 100644
--- a/crates/jp_attachment/src/lib.rs
+++ b/crates/jp_attachment/src/lib.rs
@@ -8,7 +8,6 @@ use async_trait::async_trait;
use camino::Utf8Path;
use dyn_clone::DynClone;
use dyn_hash::DynHash;
-use jp_mcp::Client;
pub use linkme::{self, distributed_slice};
use serde::{Deserialize, Serialize};
pub use typetag;
@@ -139,14 +138,7 @@ pub trait Handler: std::fmt::Debug + DynClone + DynHash + Send + Sync {
///
/// The `cwd` parameter is the current working directory, and can be used to
/// resolve relative paths.
- ///
- /// The `mcp_client` parameter is the MCP client to use for fetching
- /// resources from MCP servers, if needed.
- async fn get(
- &self,
- cwd: &Utf8Path,
- mcp_client: Client,
- ) -> Result, Box>;
+ async fn get(&self, cwd: &Utf8Path) -> Result, Box>;
}
dyn_clone::clone_trait_object!(Handler);
diff --git a/crates/jp_attachment_agentic_shepherd/Cargo.toml b/crates/jp_attachment_agentic_shepherd/Cargo.toml
index 60a55e451..3dffde60f 100644
--- a/crates/jp_attachment_agentic_shepherd/Cargo.toml
+++ b/crates/jp_attachment_agentic_shepherd/Cargo.toml
@@ -14,13 +14,12 @@ version.workspace = true
[dependencies]
jp_attachment = { workspace = true }
-jp_mcp = { workspace = true }
async-trait = { workspace = true }
camino = { workspace = true }
duct = { workspace = true }
serde = { workspace = true, features = ["derive"] }
-serde_json = { workspace = true }
+serde_json = { workspace = true, features = ["std"] }
tracing = { workspace = true }
url = { workspace = true }
diff --git a/crates/jp_attachment_agentic_shepherd/src/lib.rs b/crates/jp_attachment_agentic_shepherd/src/lib.rs
index 3e687e9f3..1f6904ae2 100644
--- a/crates/jp_attachment_agentic_shepherd/src/lib.rs
+++ b/crates/jp_attachment_agentic_shepherd/src/lib.rs
@@ -21,7 +21,6 @@ use camino::Utf8Path;
use jp_attachment::{
Attachment, BoxedHandler, HANDLERS, Handler, distributed_slice, linkme, typetag,
};
-use jp_mcp::Client;
use serde::{Deserialize, Serialize};
use tracing::debug;
use url::Url;
@@ -79,11 +78,7 @@ impl Handler for AgenticShepherd {
self.references.iter().map(Reference::to_url).collect()
}
- async fn get(
- &self,
- root: &Utf8Path,
- _: Client,
- ) -> Result, Box> {
+ async fn get(&self, root: &Utf8Path) -> Result, Box> {
debug!(
count = self.references.len(),
"Fetching agentic-shepherd attachments."
diff --git a/crates/jp_attachment_bear_note/Cargo.toml b/crates/jp_attachment_bear_note/Cargo.toml
index b2aa75a8e..36e304b62 100644
--- a/crates/jp_attachment_bear_note/Cargo.toml
+++ b/crates/jp_attachment_bear_note/Cargo.toml
@@ -15,7 +15,6 @@ version.workspace = true
[dependencies]
grizzly = { workspace = true }
jp_attachment = { workspace = true }
-jp_mcp = { workspace = true }
async-trait = { workspace = true }
camino = { workspace = true }
diff --git a/crates/jp_attachment_bear_note/src/lib.rs b/crates/jp_attachment_bear_note/src/lib.rs
index 805884f64..a7bab4bf2 100644
--- a/crates/jp_attachment_bear_note/src/lib.rs
+++ b/crates/jp_attachment_bear_note/src/lib.rs
@@ -7,7 +7,6 @@ use jp_attachment::{
Attachment, BoxedHandler, HANDLERS, Handler, distributed_slice, linkme, percent_decode_str,
percent_encode_str, typetag,
};
-use jp_mcp::Client;
use serde::{Deserialize, Serialize};
use tracing::debug;
use url::Url;
@@ -152,11 +151,7 @@ impl Handler for BearNotes {
Ok(uris)
}
- async fn get(
- &self,
- _: &Utf8Path,
- _: Client,
- ) -> Result, Box> {
+ async fn get(&self, _: &Utf8Path) -> Result, Box> {
let db = BearDb::open().map_err(|e| e.to_string())?;
let mut attachments = vec![];
diff --git a/crates/jp_attachment_cmd_output/Cargo.toml b/crates/jp_attachment_cmd_output/Cargo.toml
index 733c9582d..6068155c4 100644
--- a/crates/jp_attachment_cmd_output/Cargo.toml
+++ b/crates/jp_attachment_cmd_output/Cargo.toml
@@ -14,7 +14,6 @@ version.workspace = true
[dependencies]
jp_attachment = { workspace = true }
-jp_mcp = { workspace = true }
async-trait = { workspace = true }
camino = { workspace = true }
@@ -26,7 +25,6 @@ url = { workspace = true }
[dev-dependencies]
camino-tempfile = { workspace = true }
-indexmap = { workspace = true }
indoc = { workspace = true }
test-log = { workspace = true }
tokio = { workspace = true }
diff --git a/crates/jp_attachment_cmd_output/src/lib.rs b/crates/jp_attachment_cmd_output/src/lib.rs
index 88e7ae7ec..5d1cf05bb 100644
--- a/crates/jp_attachment_cmd_output/src/lib.rs
+++ b/crates/jp_attachment_cmd_output/src/lib.rs
@@ -6,7 +6,6 @@ use jp_attachment::{
Attachment, BoxedHandler, HANDLERS, Handler, distributed_slice, linkme, percent_decode_str,
percent_encode_str, typetag,
};
-use jp_mcp::Client;
use serde::{Deserialize, Serialize};
use url::Url;
@@ -106,11 +105,7 @@ impl Handler for Commands {
Ok(commands)
}
- async fn get(
- &self,
- root: &Utf8Path,
- _: Client,
- ) -> Result, Box> {
+ async fn get(&self, root: &Utf8Path) -> Result, Box> {
let mut attachments = vec![];
for command in &self.0 {
let cmd_line = std::iter::once(command.cmd.clone())
diff --git a/crates/jp_attachment_cmd_output/src/lib_tests.rs b/crates/jp_attachment_cmd_output/src/lib_tests.rs
index 3be90f9e6..838e572d4 100644
--- a/crates/jp_attachment_cmd_output/src/lib_tests.rs
+++ b/crates/jp_attachment_cmd_output/src/lib_tests.rs
@@ -1,4 +1,3 @@
-use indexmap::IndexMap;
use test_log::test;
use super::*;
@@ -164,9 +163,8 @@ async fn test_commands_get_missing_binary_names_command() {
);
let root = camino_tempfile::tempdir().unwrap();
- let client = Client::new(IndexMap::default());
let err = commands
- .get(root.path(), client)
+ .get(root.path())
.await
.expect_err("spawning a missing binary should error");
@@ -207,8 +205,7 @@ async fn test_commands_get() {
std::fs::write(path.join("file1"), "").unwrap();
std::fs::write(path.join("file2"), "").unwrap();
- let client = Client::new(IndexMap::default());
- let attachments = commands.get(path, client).await.unwrap();
+ let attachments = commands.get(path).await.unwrap();
assert_eq!(attachments, vec![
Attachment::text("false", indoc::indoc! {"