Skip to content

Commit d16a223

Browse files
committed
examples: four new 2026 examples + subscriptions client leg (slice 4)
New packages (all live-verified): - mrtr-elicitation-server (8642, server+client bins): full MRTR round trip — input_required → call_tool_with_input_responses → completion; README diagrams both legs and the -32003 capability gate - origin-policy-server (8643): OriginPolicy default/AllowList/Disabled via CLI flags; verified 200/403 matrix (no-Origin, evil, allowlisted, loopback); probe uses server/discover (ping is gone per SEP-2575) - header-bound-tools-server (8644): SEP-2243 x-mcp-header → Mcp-Param-Region mirroring; verified -32001 omitted/mismatch contract - bilingual-fleet-client: mixed 2025/2026 fleet sweep — verified 2026 discover negotiation + 2025 initialize fallback on one binary streamable-http-client: added the subscriptions_listen leg (ack-first filter + subscriptionId; drives trigger_changes against notification-server — deliveries verified live, unrequested prompts/list_changed correctly filtered). Proposals 1/3/5/8 dispositioned as satisfied by existing work (review doc updated). Client-using packages stay out of default-members; built by explicit gate steps in ci-gates.sh + ci.yml. EXAMPLES.md: new rows, pairing table, counts (53 active), last --example forms fixed. Full ci-gates.sh all green.
1 parent c10196f commit d16a223

18 files changed

Lines changed: 918 additions & 14 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ jobs:
7070
run: cargo test -p turul-mcp-client
7171
- name: 2026 client example (pairs with minimal-server)
7272
run: cargo build -p streamable-http-client
73+
- name: Client-using examples (not in default-members)
74+
run: cargo build -p mrtr-elicitation-server --bins -p bilingual-fleet-client
7375

7476
opt-in-2025:
7577
name: Opt-in lane (2025-11-25)

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ members = [
2222

2323
# Active examples (33 total)
2424
"examples/audit-trail-server",
25+
"examples/bilingual-fleet-client",
2526
"examples/calculator-add-builder-server",
2627
"examples/calculator-add-function-server",
2728
"examples/calculator-add-manual-server",
@@ -35,7 +36,9 @@ members = [
3536
"examples/lambda-mcp-client",
3637
"examples/lambda-mcp-server",
3738
"examples/minimal-server",
39+
"examples/mrtr-elicitation-server",
3840
"examples/notification-server",
41+
"examples/origin-policy-server",
3942
"examples/pagination-server",
4043
"examples/prompts-server",
4144
"examples/prompts-test-server",
@@ -55,6 +58,7 @@ members = [
5558
"examples/zero-config-getting-started",
5659

5760
# MCP 2025-11-25 feature showcases
61+
"examples/header-bound-tools-server",
5862
"examples/icon-showcase",
5963

6064
# Tasks E2E test examples
@@ -124,6 +128,7 @@ default-members = [
124128
"examples/derive-macro-server",
125129
"examples/function-macro-server",
126130
"examples/function-resource-server",
131+
"examples/header-bound-tools-server",
127132
"examples/icon-showcase",
128133
"examples/lambda-authorizer",
129134
"examples/middleware-auth-lambda",
@@ -132,6 +137,7 @@ default-members = [
132137
"examples/middleware-rate-limit-server",
133138
"examples/minimal-server",
134139
"examples/notification-server",
140+
"examples/origin-policy-server",
135141
"examples/oauth-resource-server",
136142
"examples/pagination-server",
137143
"examples/prompts-server",

EXAMPLES.md

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# MCP Framework Examples
22

3-
This document provides a comprehensive overview of all **50 active examples** in the MCP Framework (plus `examples/archived/` — grown by 5 in the 2026-06-12 archive slice, see `docs/plans/2026-07-28-examples-review.md`), organized by learning progression from basic concepts to advanced implementations.
3+
This document provides a comprehensive overview of all **53 active examples** in the MCP Framework (plus `examples/archived/` — grown by 5 in the 2026-06-12 archive slice, see `docs/plans/2026-07-28-examples-review.md`), organized by learning progression from basic concepts to advanced implementations.
44

55
**✅ All active examples compile under their lane's CI gates** (2026-07-28 default lane
66
+ per-manifest 2025-11-25 pins). Per-example functional re-verification against the
@@ -15,7 +15,9 @@ lane** — run the server first, then point the client at it.
1515

1616
| Client example | Lane | Corresponding server | Notes |
1717
|---|---|---|---|
18-
| `streamable-http-client` | **2026-07-28** | `minimal-server` (port 8641) | The canonical 2026 stateless pair: `connect()` negotiation, discover retention, `call_tool`, and the request-scoped progress API (shape demo — `echo` emits no progress events) |
18+
| `streamable-http-client` | **2026-07-28** | `minimal-server` (port 8641); point at `notification-server` (port 8005) for live subscription deliveries | The canonical 2026 stateless pair: `connect()` negotiation, discover retention, `call_tool`, request-scoped progress, and the ack-first `subscriptions_listen` stream |
19+
| `mrtr-elicitation-client` (in `mrtr-elicitation-server`) | **2026-07-28** | `mrtr-elicitation-server` (port 8642) | The MRTR round trip: `input_required` → answer → `call_tool_with_input_responses` |
20+
| `bilingual-fleet-client` | **both** | any mix (demo: `minimal-server` + `client-initialise-server`) | One binary sweeping a mixed 2025/2026 fleet — per-connection negotiation |
1921
| `streamable-http-client-2025-11-25` | 2025-11-25 | `client-initialise-server` (alternatives: any 2025-pinned server) | Hand-parsed 2025 POST SSE framing |
2022
| `client-initialise-report` | 2025-11-25 (wire-pinned) | `client-initialise-server` | Raw-wire lifecycle compliance probe |
2123
| `session-management-compliance-test` | 2025-11-25 (wire-pinned) | `client-initialise-server` | Session-contract regression client |
@@ -45,7 +47,7 @@ lane** — run the server first, then point the client at it.
4547
**Quick Start Command**:
4648
```bash
4749
# Start with the minimal server
48-
cargo run --example minimal-server
50+
cargo run -p minimal-server
4951
# Server: http://127.0.0.1:8641/mcp
5052
```
5153

@@ -71,7 +73,7 @@ cargo run --example minimal-server
7173
| **function-resource-server** | 8008 | ✅ VALIDATED | Function-based resources | Resource function patterns |
7274
| **session-aware-resource-server** | 8008 | ✅ VALIDATED | Session-aware resources (2025-11-25 pinned) | Session context integration on the stateful lane |
7375

74-
## 🟢 **FEATURE-SPECIFIC SERVERS** (8 examples) - Specialized MCP Features
76+
## 🟢 **FEATURE-SPECIFIC SERVERS** (11 examples) - Specialized MCP Features
7577

7678
| Example | Port | Status | Description | Key Features |
7779
|---------|------|--------|-------------|--------------|
@@ -83,6 +85,9 @@ cargo run --example minimal-server
8385
| **pagination-server** | 8044 | ✅ VALIDATED | Result pagination | Large dataset pagination support |
8486
| **notification-server** | 8005 | ✅ VALIDATED | Notifications (2026: subscriptions/listen stream) | Real-time notification patterns |
8587
| **roots-server** | 8050 | ✅ VALIDATED | Root directories | MCP roots/list endpoint demonstration |
88+
| **mrtr-elicitation-server** | 8642 | ✅ VALIDATED | MRTR elicitation round trip (2026) | `InputRequired` → retry with `inputResponses`; paired client bin |
89+
| **origin-policy-server** | 8643 | ✅ VALIDATED | Origin validation / DNS-rebinding (2026) | `OriginPolicy` default/AllowList/Disabled; 403 matrix verified live |
90+
| **header-bound-tools-server** | 8644 | ✅ VALIDATED | SEP-2243 header-bound tool params (2026) | `x-mcp-header``Mcp-Param-*` mirroring; -32001 contract verified live |
8691

8792
## 🔵 **ADVANCED/COMPOSITE SERVERS** (5 examples) - Complex Functionality
8893

@@ -99,24 +104,25 @@ cargo run --example minimal-server
99104
| **session-logging-proof-test** | 8001 | ✅ VALIDATED | Session logging validation | Session-based logging verification |
100105
| **logging-test-server** | 8052 | ✅ VALIDATED | Logging test suite | Comprehensive logging test suite |
101106

102-
## 🟠 **CLIENT EXAMPLES** (5 examples) - Client Implementation
107+
## 🟠 **CLIENT EXAMPLES** (6 examples) - Client Implementation
103108

104109
| Example | Type | Status | Description | Purpose |
105110
|---------|------|--------|-------------|---------|
106111
| **client-initialise-server** | Server | ✅ VALIDATED | Client connectivity test server | MCP session initialization testing |
107112
| **client-initialise-report** | Client | ✅ VALIDATED | MCP client implementation | Tests server initialization |
108113
| **streamable-http-client** | Client | ✅ VALIDATED | 2026-07-28 stateless client (pairs with minimal-server) | connect() negotiation, discover retention, request-scoped progress API |
109114
| **streamable-http-client-2025-11-25** | Client | ✅ VALIDATED | Hand-parsed 2025 Streamable HTTP client | MCP 2025-11-25 streaming demo |
115+
| **bilingual-fleet-client** | Client | ✅ VALIDATED | Mixed 2025/2026 fleet sweep | Per-connection negotiation (server/discover → initialize fallback) |
110116
| **logging-test-client** | Client | ✅ VALIDATED | Logging client | Tests logging functionality |
111117
| **session-management-compliance-test** | Combined | ✅ VALIDATED | Session compliance testing | MCP session spec compliance |
112118

113119
**Client Testing**:
114120
```bash
115121
# Start the test server
116-
cargo run --example client-initialise-server
122+
cargo run -p client-initialise-server
117123

118124
# Test with client (in another terminal)
119-
cargo run --example client-initialise-report -- --url http://127.0.0.1:8641/mcp
125+
cargo run -p client-initialise-report -- --url http://127.0.0.1:8641/mcp
120126
```
121127

122128
## ☁️ **AWS LAMBDA** (4 examples) - Serverless Deployment
@@ -156,10 +162,10 @@ cargo run --example client-initialise-report -- --url http://127.0.0.1:8641/mcp
156162
**Task E2E Testing**:
157163
```bash
158164
# Start the task-enabled server
159-
cargo run --example tasks-e2e-inmemory-server
165+
cargo run -p tasks-e2e-inmemory-server
160166

161167
# Run the client test suite (in another terminal)
162-
cargo run --example tasks-e2e-inmemory-client -- --url http://127.0.0.1:8080/mcp
168+
cargo run -p tasks-e2e-inmemory-client -- --url http://127.0.0.1:8080/mcp
163169
```
164170

165171
## 📖 **TYPE SHOWCASES** (4 examples) - Print-Only Demonstrations
@@ -207,9 +213,9 @@ re-verified its six examples live on the wire.
207213
- **Zero Breaking Changes**: All existing examples continue to work
208214

209215
### 📊 **Statistics**
210-
- **Total Examples**: 58 (25 archived in `examples/archived/`)
216+
- **Total Examples**: 53 active (30 archived in `examples/archived/`)
211217
- **Session-Aware Resources**: 6 examples demonstrating session context integration
212-
- **Client-Server Pairs**: 5 examples validating communication patterns
218+
- **Client-Server Pairs**: 9 pairing-table rows validating communication patterns
213219
- **Task Support**: 3 examples demonstrating MCP 2025-11-25 task lifecycle (InMemory storage)
214220
- **Middleware**: 4 examples (HTTP auth, logging, rate-limiting, Lambda auth)
215221
- **Storage Backends**: All 4 session backends (InMemory, SQLite, PostgreSQL, DynamoDB) working
@@ -219,11 +225,11 @@ re-verified its six examples live on the wire.
219225

220226
**Basic Pattern**:
221227
```bash
222-
# Run any example
223-
cargo run --example <example-name>
228+
# Run any example (examples are packages with [[bin]] targets — use -p)
229+
cargo run -p <example-name>
224230

225231
# Examples with custom ports
226-
cargo run --example client-initialise-server -- --port 8641
232+
cargo run -p client-initialise-server -- --port 8641
227233
```
228234

229235
**With Features** (for PostgreSQL/DynamoDB examples):

docs/plans/2026-07-28-examples-review.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,39 @@ Each proposal names **why it should exist**, the **learning value** for a human
191191
reader, the **spec match** (the driver-doc table it demonstrates), and its
192192
**practical usefulness**. None exist today in any form.
193193

194+
> **EXECUTED 2026-06-12** (slice 4) — per-proposal disposition:
195+
>
196+
> 1. **subscriptions pair** — satisfied without a new package: the
197+
> notification-server rewrite (slice 2) is the server side;
198+
> `streamable-http-client` gained the client leg (`subscriptions_listen`
199+
> ack + filter + `subscriptionId`, drives `trigger_changes` when pointed
200+
> at notification-server). Live-verified: deliveries land, the
201+
> un-requested `prompts/list_changed` is correctly filtered out.
202+
> 2. **mrtr-elicitation-server** — BUILT (port 8642, server + client bins).
203+
> Live-verified full round trip: `input_required`
204+
> `call_tool_with_input_responses` → "deployed billing-api ✅".
205+
> 3. **request-scoped-observability** — satisfied without a new package:
206+
> notification-server's `long_job` (progress + log under the per-request
207+
> opt-ins) plus `streamable-http-client`'s `call_tool_with_progress`.
208+
> 4. **origin-policy-server** — BUILT (port 8643, was "browser-facing-server").
209+
> Live-verified matrix: no-Origin 200 / evil 403 / allowlisted 200 /
210+
> loopback 200.
211+
> 5. **oauth-scoped-server** — folded into `oauth-resource-server` as the
212+
> audit suggested (`--required-scope` via manual middleware construction,
213+
> slice 3); no new package.
214+
> 6. **bilingual-fleet-client** — BUILT. Live-verified sweep: 2026 peer
215+
> negotiates stateless (discover retained), 2025 peer falls back to the
216+
> initialize handshake; same `list_tools` works on both.
217+
> 7. **header-bound-tools-server** — BUILT (port 8644). Live-verified
218+
> SEP-2243 contract: matching header 200; omitted/mismatched header →
219+
> 400 + `-32001` with the exact mismatch messages.
220+
> 8. **completion-provider-server** — satisfied by the completion-server
221+
> rewrite (slice 2); no new package.
222+
>
223+
> Client-using packages (`mrtr-elicitation-server`, `bilingual-fleet-client`)
224+
> stay out of default-members and are built by an explicit gate step
225+
> (ci-gates.sh + ci.yml), like `streamable-http-client`.
226+
194227
### 1. `subscriptions-listen-server` + `subscriptions-listen-client` (pair)
195228

196229
- **Why**: `subscriptions/listen` is THE replacement for the entire 2025
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Uses the bilingual turul-mcp-client (out of default-members, like
2+
# streamable-http-client); built by an explicit gate step.
3+
[package]
4+
name = "bilingual-fleet-client"
5+
version = "0.4.0"
6+
edition.workspace = true
7+
authors.workspace = true
8+
license.workspace = true
9+
10+
[dependencies]
11+
turul-mcp-client.workspace = true
12+
tokio.workspace = true
13+
tracing.workspace = true
14+
tracing-subscriber.workspace = true
15+
serde_json.workspace = true
16+
17+
[[bin]]
18+
name = "bilingual-fleet-client"
19+
path = "src/main.rs"
20+
test = false
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
//! # Bilingual Fleet Client
2+
//!
3+
//! One client binary talking to a mixed 2025-11-25 / 2026-07-28 fleet — the
4+
//! rolling-upgrade story. For each URL the bilingual `McpClient` probes
5+
//! `server/discover`; a 2026 server answers, a 2025 server returns `-32601`
6+
//! (or `-32004`) and the client falls back to the `initialize` handshake.
7+
//! The negotiated wire spec then locks for that connection's lifetime.
8+
//!
9+
//! Upgrade sequencing this enables: upgrade servers first (the client
10+
//! negotiates each one independently), clients are never blocked.
11+
//!
12+
//! ```bash
13+
//! # Start one server from each generation:
14+
//! cargo run -p minimal-server # 2026-07-28, port 8641
15+
//! cargo run -p client-initialise-server -- --port 52940 # 2025-11-25
16+
//!
17+
//! # Then sweep the fleet:
18+
//! cargo run -p bilingual-fleet-client -- \
19+
//! http://127.0.0.1:8641/mcp http://127.0.0.1:52940/mcp
20+
//! ```
21+
22+
use turul_mcp_client::transport::HttpTransport;
23+
use turul_mcp_client::{McpClient, McpVersion};
24+
25+
#[tokio::main]
26+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
27+
tracing_subscriber::fmt()
28+
.with_env_filter(
29+
tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "warn".into()),
30+
)
31+
.init();
32+
33+
let urls: Vec<String> = std::env::args().skip(1).collect();
34+
let urls = if urls.is_empty() {
35+
println!("No URLs given — using the default demo fleet.");
36+
println!("(start them with: cargo run -p minimal-server and");
37+
println!(" cargo run -p client-initialise-server -- --port 52940)\n");
38+
vec![
39+
"http://127.0.0.1:8641/mcp".to_string(),
40+
"http://127.0.0.1:52940/mcp".to_string(),
41+
]
42+
} else {
43+
urls
44+
};
45+
46+
for url in &urls {
47+
println!("── {url}");
48+
match probe(url).await {
49+
Ok(()) => {}
50+
Err(e) => println!(" unreachable: {e}"),
51+
}
52+
println!();
53+
}
54+
55+
Ok(())
56+
}
57+
58+
/// Connect, report the negotiated spec, and run one version-appropriate call.
59+
async fn probe(url: &str) -> Result<(), Box<dyn std::error::Error>> {
60+
let transport = Box::new(HttpTransport::new(url)?);
61+
let client = McpClient::new(transport, Default::default());
62+
client.connect().await?;
63+
64+
let version = client.negotiated_version().await;
65+
match version {
66+
Some(McpVersion::V2026_07_28) => {
67+
println!(" negotiated: 2026-07-28 (server/discover answered — stateless wire)");
68+
// 2026-only surface: the retained discover body.
69+
if let Some(d) = client.discovered_server().await {
70+
println!(" serverInfo: {:?}", d.server_info);
71+
println!(" supported : {:?}", d.supported_versions);
72+
}
73+
}
74+
Some(other) => {
75+
println!(
76+
" negotiated: {other:?} (discover refused — fell back to the initialize handshake; Mcp-Session-Id session is live)"
77+
);
78+
}
79+
None => {
80+
println!(" negotiation failed — no common spec");
81+
return Ok(());
82+
}
83+
}
84+
85+
// Version-neutral surface: the same list_tools call works on both wires.
86+
let tools = client.list_tools().await?;
87+
let names: Vec<&str> = tools.iter().map(|t| t.name.as_str()).collect();
88+
println!(" tools ({}): {names:?}", names.len());
89+
90+
// disconnect() is a no-op on 2026 (nothing to tear down) and a DELETE on
91+
// a 2025 session — the client routes by the negotiated version.
92+
client.disconnect().await?;
93+
Ok(())
94+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[package]
2+
name = "header-bound-tools-server"
3+
version = "0.4.0"
4+
edition.workspace = true
5+
authors.workspace = true
6+
license.workspace = true
7+
8+
[dependencies]
9+
turul-mcp-server.workspace = true
10+
turul-mcp-protocol.workspace = true
11+
turul-mcp-builders.workspace = true
12+
async-trait.workspace = true
13+
tokio.workspace = true
14+
tracing.workspace = true
15+
tracing-subscriber.workspace = true
16+
serde_json.workspace = true
17+
18+
[[bin]]
19+
name = "header-bound-tools-server"
20+
path = "src/main.rs"
21+
test = false

0 commit comments

Comments
 (0)