elicitation 0.10.0

Conversational elicitation of strongly-typed Rust values via MCP
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
# Elicitation

> **Formally verified type-safe state machines. Program invariants you can prove.**

[![Crates.io](https://img.shields.io/crates/v/elicitation.svg)](https://crates.io/crates/elicitation)
[![Documentation](https://docs.rs/elicitation/badge.svg)](https://docs.rs/elicitation)
[![License](https://img.shields.io/badge/license-Apache--2.0%20OR%20MIT-blue.svg)](LICENSE-APACHE)
[![Rust](https://img.shields.io/badge/rust-2024-orange.svg)](https://www.rust-lang.org)

---

## The Story

### Step 1: Verifying types (boring, but foundational)

Formal verification research focuses almost entirely on methods — and for good
reason. Methods are interesting; types are not. Who cares that an `i8` is an `i8`?

And yet: verifying types is tractable. A constrained type like `PortNumber` or
`StringNonEmpty` has a crisp, machine-checkable invariant. When a type implements
`Elicitation`, it gains three proof methods as part of the trait — each returning a
`TokenStream` that the corresponding toolchain can verify:

```rust
#[cfg(feature = "proofs")]
fn kani_proof()    -> proc_macro2::TokenStream { /* symbolic execution proof */ }
fn verus_proof()   -> proc_macro2::TokenStream { /* SMT specification proof  */ }
fn creusot_proof() -> proc_macro2::TokenStream { /* deductive contract proof  */ }
```

These are not annotations on a separate proof file. The type *carries* its proof.
For user-defined types, `#[derive(Elicit)]` composes the proof automatically from
the proofs of the constituent field types — add a field, get its proof for free.
Compose two types and you have the materials to compose their proofs.

### Step 2: State machines (not boring at all)

It turns out that verifying types is only a little harder than verifying state
machines — and state machines are far more interesting than plain types. The
contracts system makes state transitions first-class:

```rust
pub struct DbConnected;    // proposition: a connection is open
pub struct QueryExecuted;  // proposition: a query ran successfully
impl Prop for DbConnected {}
impl Prop for QueryExecuted {}
```

`Established<P>` is a zero-sized proof token that proposition `P` holds.
It cannot be constructed except by code that actually performed the work. The
compiler then enforces transition order: you cannot call a function requiring
`Established<QueryExecuted>` without first holding `Established<DbConnected>`.

This is a formally proven type-safe state machine — and the proofs compose. If
`DbConnected` has a verified proof and `QueryExecuted` has a verified proof, their
conjunction `And<DbConnected, QueryExecuted>` has a verified proof too, via
`both()`.

### Step 3: Methods that are correct by construction (the payoff)

State machines compose into methods. The main thing preserved across every proof is
a **program invariant** — a property of the system that holds regardless of what
path execution took to get there.

With invariants expressed in the type system and verified at each step, developer
goals can be projected into the type space: define what "ready to deploy" means as
a proposition, write the functions that establish its preconditions, and the
compiler guarantees that the deployment function can only be called once all
invariants are satisfied.

The agent's role in this is proof search: given a goal type, find the sequence of
verified operations that transforms the current state into the desired state. Every
step is an auditable tool call with a known contract. The resulting tool chain *is*
the method — correct by construction, not verified after the fact.

---

## Architecture

The framework has three layers:

```
┌─────────────────────────────────────────────────────────┐
│  Your domain types                                       │
│  #[derive(Elicit)]  →  agent-navigable, MCP-crossing     │
├─────────────────────────────────────────────────────────┤
│  Shadow crates  (elicit_*)                               │
│  Verified vocabularies for third-party libraries         │
│  Types + Methods + Traits = the agent's dictionary       │
├─────────────────────────────────────────────────────────┤
│  Contracts  (Prop / Established<P> / And<P,Q>)           │
│  Postconditions chain into preconditions                 │
│  Workflow correctness enforced at the type level         │
└─────────────────────────────────────────────────────────┘
```

All three layers are formally verified by Kani, Creusot, and Verus.

---

## Layer 1: Your Types Become Agent-Native

For your own domain types, `#[derive(Elicit)]` is all you need. MCP requires all
values that cross the boundary to be `Serialize + DeserializeOwned + JsonSchema`, so
your type must derive all four — the compiler may not always catch a missing impl,
but you will get runtime errors if a type is not fully wired:

```rust
use serde::{Serialize, Deserialize};
use schemars::JsonSchema;
use elicitation::Elicit;

// All four derives are required for MCP tool use
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Elicit)]
#[prompt("Configure your deployment")]
#[spec_summary("Deployment configuration with environment and scale settings")]
pub struct DeployConfig {
    #[prompt("Which environment?")]
    pub environment: Environment,   // must also impl Elicitation

    #[prompt("Number of replicas (1–16):")]
    #[spec_requires("replicas >= 1 && replicas <= 16")]
    pub replicas: u8,

    #[prompt("Container image URI:")]
    pub image: String,

    #[skip]
    pub _internal_id: u64,          // not elicited; omitted from MCP interaction
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Elicit)]
pub enum Environment {
    Production,
    Staging,
    Development,
}
```

`#[derive(Elicit)]` generates the full `Elicitation` impl — the `elicit()` async
method, the `Prompt` impl, and the proof functions — by composing the impls of the
constituent field types. This is why `Elicit` can only be derived on types whose
fields already implement `Elicitation`: the derive has nothing to compose from if
a field type is unknown to the framework.

This is also why the core `elicitation` crate manually implements `Elicitation` for
third-party types behind feature gates (e.g. `features = ["sqlx-types"]`): those
types don't derive it themselves, so we provide the impl so they can participate as
fields. The `elicit_*` shadow crates build on top of that foundation — they add
newtype wrappers and expose methods and traits as MCP tools, but require the
`Elicitation` support to already be in place in the core crate.

### Derived proof functions

When you derive `Elicit`, your type automatically gets working `kani_proof()`,
`verus_proof()`, and `creusot_proof()` methods (with `feature = "proofs"`). The
derive walks every field type and extends its own proof token stream with each
field's proof:

```rust
// What the derive generates for DeployConfig:
fn kani_proof() -> proc_macro2::TokenStream {
    let mut ts = TokenStream::new();
    ts.extend(<Environment as Elicitation>::kani_proof());
    ts.extend(<u8 as Elicitation>::kani_proof());
    ts.extend(<String as Elicitation>::kani_proof());
    ts
}
```

A struct's proof is the union of its parts. Add a field, get its proof for free.

### Style System

Every type ships with default prompts, but the Style system means you are never
locked into them. The classic use case is **human vs. AI audience**: a terse
machine-readable prompt is noise to a human; a friendly wizard-style prompt is
equally wasteful to an agent that just needs the field name and constraints.

You don't implement anything — you annotate fields with `#[prompt]` and name
the styles you need. The derive generates the style enum for you:

```rust
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Elicit)]
pub struct DeployConfig {
    // Default prompt (used when no style is active)
    #[prompt("environment")]
    // Named style overrides — derive generates DeployConfigElicitStyle::{ Default, Human, Agent }
    #[prompt("Which environment should we deploy to?", style = "human")]
    #[prompt("environment: production|staging|development", style = "agent")]
    pub environment: Environment,

    #[prompt("replicas")]
    #[prompt("How many replicas? (1–16, default 2):", style = "human")]
    #[prompt("replicas: u8 1..=16", style = "agent")]
    pub replicas: u8,
}
```

Then hot-swap the style per session at the call site — no changes to the type:

```rust
// Human session
let result = DeployConfig::elicit(
    &client.with_style::<DeployConfig, _>(DeployConfigElicitStyle::Human)
).await?;

// Agent session
let result = DeployConfig::elicit(
    &client.with_style::<DeployConfig, _>(DeployConfigElicitStyle::Agent)
).await?;
```

`with_style::<T, _>(style)` works on **any** type `T` with an `Elicitation`
impl, including third-party types the core crate ships impls for. This is the
escape hatch from vendor lock-in: our default prompts are a starting point, not
a constraint.

### Generators

The `Generator` trait is a simple contract: given configuration, produce values
of a target type.

```rust
pub trait Generator {
    type Target;
    fn generate(&self) -> Self::Target;
}
```

Any struct that knows how to produce a `T` can implement it — the source is
entirely up to you. A sensor fusion generator that reads temperature and
humidity to derive barometric pressure, a lookup-table interpolator, a physics
simulation step — all of these are just "methods that generate at `T`", and
they compose identically with everything else in the framework.

```rust
struct BarometricGenerator { altitude_m: f32, lapse_rate: f32 }

impl Generator for BarometricGenerator {
    type Target = Pressure;

    fn generate(&self) -> Pressure {
        let p = SEA_LEVEL_PA * (1.0 - self.lapse_rate * self.altitude_m / 288.15).powf(5.2561);
        Pressure::new(p)
    }
}
```

Because `Generator` is a plain trait, implementations can be exposed as MCP
tools through the shadow crate machinery — agents can request "generate a
`Pressure` reading" without knowing the formula or the sensor source. The
pattern also fits workflows where the agent elicits the *strategy* once and
the program drives generation many times from it:

```rust
let mode = InstantGenerationMode::elicit(communicator).await?;
let generator = InstantGenerator::new(mode);

let t1 = generator.generate();
let t2 = generator.generate();
```

The `elicitation_rand` crate extends this with the `Rand` trait and
`#[rand(...)]` field attributes that encode the same constraints as the type's
formal proofs directly into the sampling strategy — `bounded(L, H)`,
`positive`, `nonzero`, `even`, `odd`, and `and(A, B)` / `or(A, B)`. The
derive generates a seeded `random_generator(seed: u64)` for deterministic,
reproducible output. `elicitation_rand` ships its own Kani suite proving
generators satisfy their declared contracts.

### Action traits: the grammar of elicitation

Every `Elicitation` impl is built on one of three **action traits** that describe
the interaction paradigm. These are the primitives from which all elicitation
behaviour is composed, and they are formally verified:

| Trait | Paradigm | Typical use |
|---|---|---|
| `Select` | Choose one from a finite set | Enums, categorical fields |
| `Affirm` | Binary yes/no confirmation | `bool` fields, guard steps |
| `Survey` | Sequential multi-field elicitation | Structs, configuration objects |

`#[derive(Elicit)]` automatically assigns the correct action trait — enums with
unit variants get `Select`, `bool` fields get `Affirm`, structs get `Survey` —
and the derived state machine sequences the interactions accordingly.

Together with the contract types (`Prop`, `Established<P>`, `And<P,Q>`), the
action traits provide the **grammar for constructing formally verified state
transitions**: `Select` constrains the domain of a transition to a known finite
set, `Affirm` guards a transition on a binary condition, and `Survey` sequences
a set of field transitions into a single composite step. Each interaction has a
verified proof; the composition of interactions inherits those proofs.

---

## Layer 2: Shadow Crates — The Agent's Dictionary

A **shadow crate** (`elicit_*`) is a crate-shaped vocabulary for a third-party library.
It exposes three things:

| Layer | What it provides | Mechanism |
|---|---|---|
| **Types** | `serde` + `JsonSchema` wrappers so values cross the MCP boundary | Newtypes |
| **Methods** | Instance methods exposed as MCP tools | `#[reflect_methods]` |
| **Traits** | Third-party trait methods as typed factories | `#[reflect_trait]` |

Together, these form a **complete vocabulary** for the library. An agent with access
to all three layers can reason about and compose the library's behaviour without
writing a single line of Rust.

### Three Tool Exposure Mechanisms

**`#[reflect_methods]`** — for a newtype with methods you want the agent to call:

```rust
use elicitation_derive::reflect_methods;

#[reflect_methods]
pub struct ElicitArg(pub clap::Arg);
// Generates: arg__get_long, arg__get_short, arg__get_help, ... as MCP tools
```

**`#[reflect_trait]`** — for a third-party trait whose methods are worth calling on
any registered `T: FooTrait`:

```rust
use elicitation_macros::reflect_trait;

#[reflect_trait(clap::ValueEnum)]
pub trait ValueEnumTools {
    fn value_variants(&self) -> Vec<PossibleValue>;
}
// Generates a typed factory: any T: ValueEnum gets value_variants exposed as a tool
```

**Fragment tools + `EmitCode`** — for compile-time macros that cannot run at MCP
time (e.g. `sqlx::query!`, `sqlx::migrate!`). The agent calls a fragment tool that
emits verified Rust source via `EmitCode`. The emitted code is compiled into the
consumer binary where the macro runs with a live database connection:

```rust
#[elicit_tool(
    plugin = "sqlx_frag",
    name   = "query",
    description = "Emit a sqlx::query! call. Establishes: QueryFragmentEmitted.",
    emit_ctx("ctx.db_url" => r#"std::env::var("DATABASE_URL").expect("DATABASE_URL")"#),
)]
async fn emit_query(ctx: Arc<PluginContext>, p: QueryParams) -> Result<CallToolResult, ErrorData> {
    // p.sql is emitted as a sqlx::query!(p.sql, args...) TokenStream
    // ...
}
```

### Available Shadow Crates

| Crate | Library | What it covers |
|---|---|---|
| `elicit_reqwest` | `reqwest` | HTTP client: fetch, post, auth, pagination, workflow |
| `elicit_sqlx` | `sqlx` | Database: connect, execute, fetch, transactions, query fragments |
| `elicit_tokio` | `tokio` | Async: sleep, timeout, semaphores, barriers, file I/O |
| `elicit_clap` | `clap` | CLI: `Arg`, `Command`, `ValueEnum`, `PossibleValue` |
| `elicit_chrono` | `chrono` | Datetimes, durations, timezones |
| `elicit_jiff` | `jiff` | Temporal arithmetic |
| `elicit_time` | `time` | Date/time primitives |
| `elicit_url` | `url` | URL construction and validation |
| `elicit_regex` | `regex` | Pattern matching |
| `elicit_uuid` | `uuid` | UUID generation |
| `elicit_serde_json` | `serde_json` | JSON values, maps, dynamic typing |
| `elicit_std` | `std` | Selected stdlib types |

---

## Layer 3: Contracts — Workflow Correctness at the Type Level

The contracts system makes workflow preconditions impossible to violate at compile
time. A `Prop` is a marker trait; `Established<P>` is a zero-sized proof token that
`P` holds; `And<P,Q>` composes proofs; `both()` constructs a conjunction.

```rust
use elicitation::contracts::{Prop, Established, And, both};

// Domain propositions — unit structs that act as type-level facts
pub struct DbConnected;
pub struct QueryExecuted;
impl Prop for DbConnected {}
impl Prop for QueryExecuted {}

// A function that REQUIRES proof of connection
fn fetch_rows(
    sql: &str,
    _pre: Established<DbConnected>,   // caller must supply this
) -> Vec<Row> {
    // ...
}

// A function that PRODUCES proof of connection
fn connect(url: &str) -> (Pool, Established<DbConnected>) {
    let pool = Pool::connect(url);
    (pool, Established::assert())     // assert: we just did the work
}

// Composing proofs
let (pool, db_proof) = connect(&url);
let (_, query_proof) = execute_query(&pool, db_proof);
let both_proof: Established<And<DbConnected, QueryExecuted>> =
    both(db_proof, query_proof);
```

Shadow crates ship their own domain propositions. `elicit_sqlx` provides
`DbConnected`, `QueryExecuted`, `RowsFetched`, `TransactionOpen`,
`TransactionCommitted`, `TransactionRolledBack`. `elicit_reqwest` provides
`UrlValid`, `RequestCompleted`, `StatusSuccess`, `Authorized`, and the composite
`FetchSucceeded = And<UrlValid, And<RequestCompleted, StatusSuccess>>`.

The `Tool` trait formalises this pattern for composable workflow steps:

```rust
pub trait Tool {
    type Input: Elicitation;
    type Output;
    type Pre: Prop;
    type Post: Prop;

    async fn execute(
        &self,
        input: Self::Input,
        pre: Established<Self::Pre>,
    ) -> ElicitResult<(Self::Output, Established<Self::Post>)>;
}
```

Sequential composition (`then`) and parallel composition (`both_tools`) are
provided, with the type system enforcing that each step's `Post` satisfies the
next step's `Pre`.

---

## Formal Verification

Every type that implements `Elicitation` can carry proofs for three independent
verifiers (with the `proofs` feature). The default implementations return empty
token streams; concrete implementations emit verified source:

```rust
// A constrained integer type carrying its own proofs
impl Elicitation for PortNumber {
    // ...elicit(), prompt(), etc...

    #[cfg(feature = "proofs")]
    fn kani_proof() -> proc_macro2::TokenStream {
        quote! {
            #[kani::proof]
            fn verify_port_number_bounds() {
                let n: u16 = kani::any();
                kani::assume(n >= 1024 && n <= 65535);
                let port = PortNumber::new(n).unwrap();
                assert!(*port >= 1024 && *port <= 65535);
            }
        }
    }

    #[cfg(feature = "proofs")]
    fn verus_proof() -> proc_macro2::TokenStream {
        quote! {
            proof fn port_number_invariant(n: u16)
                requires 1024 <= n <= 65535
                ensures PortNumber::new(n).is_some()
            { /* ... */ }
        }
    }
}
```

### The three verifiers

| Verifier | Approach | Strength | Coverage |
|---|---|---|---|
| **Kani** | Bounded model checking / symbolic execution | Exhaustive within bound; finds real bugs | 388 passing harnesses |
| **Verus** | SMT-based program logic | Arithmetic and data structure properties | 158 passing proofs |
| **Creusot** | Deductive verification with Pearlite annotations | Rich invariants; compositional across functions | 22,837 valid goals across 19 modules |

Results are tracked in `*_verification_results.csv`.

### Trust the source, verify the wrapper

Proofs are scoped to *our* logic, not third-party internals:

- **Trust the source**`std::collections::HashMap` correctly stores keys;
  `clap::ColorChoice` has the variants its docs claim. That is the upstream
  library's responsibility.
- **Verify the wrapper** — our `from_label()` roundtrip is complete; our
  `Established::assert()` calls appear only after the real work succeeds; our
  contracts accurately describe what each operation establishes.

This keeps proofs tractable, focused, and composable. A proof that accepts a
third-party invariant via `kani::assume` and asserts our dispatch contract on top
is a valid, composable proof node — not a shortcut.

### Composing proofs across the system

Because proof methods live on the type, they compose naturally. Two types'
`kani_proof()` token streams can be combined into a single proof harness that
verifies both together. The contracts system (`Prop`/`Established`/`And`) provides
the state-machine layer; the proof methods provide the type-invariant layer. Both
compose independently and together.

Run verification with:

```bash
just verify-kani-tracked       # Kani — all harnesses with CSV tracking
just verify-kani <harness>     # Single harness
just verify-creusot <file.rs>  # Creusot
just verify-verus-tracked      # Verus
```

---

## Visibility

Formal proofs tell you what properties hold. The visibility layer tells you
*what is actually running* — and makes that information available to both
developers and agents at every level, from static type structure to production
telemetry.

### TypeSpec — contracts on demand

Every elicitation type implements the `ElicitSpec` trait, which is built
alongside the `anodized::spec` `#[spec]` annotations on its constructors —
keeping the formal conditions and the browsable spec in sync by construction.

`TypeSpecPlugin` surfaces these specs as MCP tools using a **lazy dictionary**
pattern: agents pull only the spec slice they need rather than flooding the
context window with schema dumps.

| Tool | What it returns |
|---|---|
| `type_spec__describe_type` | Summary + list of available spec categories |
| `type_spec__explore_type` | One category in full: `requires`, `ensures`, `bounds`, `fields` |

Types register themselves via `inventory::submit!` when `#[derive(Elicit)]` is
used, so the dictionary stays current with the codebase automatically.

### TypeGraph — structure at a glance

`TypeGraphPlugin` (feature: `graph`) renders the structural hierarchy of
registered types as Mermaid diagrams or DOT graphs — without reading source code.

| Tool | What it returns |
|---|---|
| `type_graph__list_types` | All registered graphable type names |
| `type_graph__graph_type` | Mermaid or DOT graph rooted at a given type |
| `type_graph__describe_edges` | Human-readable edge summary for one type |

`#[derive(Elicit)]` on non-generic types auto-registers them via
`inventory::submit!(TypeGraphKey)`. An agent can call `list_types()` to
discover the vocabulary, then `graph_type("ApplicationConfig")` to see how
`NetworkConfig`, `Role`, and `DeploymentMode` compose into it — all in a
single tool call.

### ElicitIntrospect — stateless observability

`ElicitIntrospect` is a trait extending `Elicitation` that exposes static
structural metadata for production instrumentation:

```rust
pub trait ElicitIntrospect: Elicitation {
    fn pattern()  -> ElicitationPattern;  // Survey / Select / Affirm / Primitive
    fn metadata() -> TypeMetadata;        // type_name, description, fields/variants
}
```

Both methods are **pure functions with zero allocation** — ideal for labelling
spans and metrics without overhead:

```rust
// Add type structure to OpenTelemetry / tracing spans
#[tracing::instrument(skip(communicator), fields(
    type_name = %T::metadata().type_name,
    pattern   = %T::pattern().as_str(),
))]
async fn elicit_with_tracing<T: ElicitIntrospect>(
    communicator: &impl ElicitCommunicator
) -> ElicitResult<T> {
    T::elicit(communicator).await
}

// Prometheus counter: one metric, labelled by type + pattern
ELICITATION_COUNTER
    .with_label_values(&[T::metadata().type_name, T::pattern().as_str()])
    .inc();
```

`#[derive(Elicit)]` generates the `ElicitIntrospect` impl automatically,
composing field and variant metadata from the constituent types. The example
`observability_introspection.rs` walks through tracing, metrics, agent
planning, and nested introspection patterns in full.

---

## Getting Started

```toml
[dependencies]
elicitation = "0.9"
rmcp        = "1"
schemars    = "0.8"
serde       = { version = "1", features = ["derive"] }
tokio       = { version = "1", features = ["full"] }
```

### Step 1 — derive `Elicit` on your domain types

`#[derive(Elicit)]` is the entire declaration. It generates the MCP
round-trip, schema, and the `elicit_checked()` method. Works on structs
(Survey paradigm) and enums (Select paradigm) alike:

```rust
use elicitation::Elicit;
use serde::{Deserialize, Serialize};

/// Finite choice — derives the Select paradigm
#[derive(Debug, Clone, Serialize, Deserialize, Elicit)]
pub enum Difficulty {
    Easy,
    Normal,
    Hard,
}

/// Multi-field form — derives the Survey paradigm  
#[derive(Debug, Clone, Serialize, Deserialize, Elicit)]
pub struct PlayerProfile {
    #[prompt("Enter your name:")]
    pub name: String,
    #[prompt("Pick a difficulty:")]
    pub difficulty: Difficulty,
    pub score: u32,
}
```

### Step 2 — call `elicit_tools!` inside your server impl

`elicitation::elicit_tools! { Type, ... }` expands inline inside your
`#[tool_router]` impl block. It generates one interactive `elicit_*` tool
per listed type — no extra attributes, no separate structs:

```rust
use elicitation::{ChoiceSet, ElicitServer, Elicitation};
use rmcp::handler::server::wrapper::Parameters;
use rmcp::model::{CallToolResult, Content, ServerCapabilities, ServerInfo};
use rmcp::service::{Peer, RoleServer};
use rmcp::{ErrorData, ServerHandler, tool, tool_handler, tool_router};
use rmcp::handler::server::router::tool::ToolRouter;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct StartGameRequest {
    pub session_id: String,
}

pub struct GameServer {
    tool_router: ToolRouter<Self>,
}

#[tool_router]
impl GameServer {
    pub fn new() -> Self {
        Self { tool_router: Self::tool_router() }
    }

    /// Regular tool — structured params, no interaction needed
    #[tool(description = "Start a new game session")]
    pub async fn start_game(
        &self,
        Parameters(req): Parameters<StartGameRequest>,
    ) -> Result<CallToolResult, ErrorData> {
        Ok(CallToolResult::success(vec![
            Content::text(format!("Session {} started", req.session_id)),
        ]))
    }

    /// Interactive tool — gate the LLM inside a walled garden of valid moves
    #[tool(description = "Play a move. You will be prompted to choose a difficulty.")]
    pub async fn play(
        &self,
        peer: Peer<RoleServer>,
    ) -> Result<CallToolResult, ErrorData> {
        // Wrap the peer in ElicitServer to drive the interactive round-trip
        let server = ElicitServer::new(peer);

        // Elicit a free-form struct from the LLM / user
        let profile = PlayerProfile::elicit(&server).await
            .map_err(|e| ErrorData::internal_error(e.to_string(), None))?;

        // Or: constrain to a runtime-computed set of valid options
        let options = vec![1u32, 5, 10, 25];
        let bet = ChoiceSet::new(options)
            .with_prompt("Choose your bet:")
            .elicit(&server)
            .await
            .map_err(|e| ErrorData::internal_error(e.to_string(), None))?;

        Ok(CallToolResult::success(vec![
            Content::text(format!("{} bets {} on {:?}", profile.name, bet, profile.difficulty)),
        ]))
    }

    // Auto-generate elicit_difficulty and elicit_player_profile tools
    elicitation::elicit_tools! {
        Difficulty,
        PlayerProfile,
    }
}

#[tool_handler(router = self.tool_router)]
impl ServerHandler for GameServer {
    fn get_info(&self) -> ServerInfo {
        ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
    }
}
```

### Step 3 — serve

```rust
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let server = GameServer::new();
    rmcp::service::serve_server(server, rmcp::transport::stdio()).await?;
    Ok(())
}
```

The registered tools: `start_game`, `play`, `elicit_difficulty`,
`elicit_player_profile`. The LLM calls `elicit_difficulty` and gets back a
validated `Difficulty` it can pass anywhere. `ChoiceSet` traps it inside
only the options you allow at runtime — the walled garden pattern.

### Adding shadow crate plugins

Shadow crates expose third-party libraries as pre-built tool sets. Add them
alongside your own server via `PluginRegistry`:

```rust
use elicitation::PluginRegistry;
use rmcp::ServiceExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    PluginRegistry::new()
        .register_flat(GameServer::new())
        .register("http", elicit_reqwest::WorkflowPlugin::default_client())
        .register("db",   elicit_sqlx::SqlxWorkflowPlugin::default())
        .serve(rmcp::transport::stdio())
        .await?;
    Ok(())
}
```

This exposes `http__get`, `db__connect`, `db__query` alongside your own
tools — one registry, one transport, zero glue code.

---

## Workspace Crate Map

| Crate | Role |
|---|---|
| `elicitation` | Core library: traits, contracts, verification types, MCP plumbing |
| `elicitation_derive` | Proc macros: `#[derive(Elicit)]`, `#[elicit_tool]`, `#[reflect_methods]` |
| `elicitation_macros` | Additional macros: `#[reflect_trait]` |
| `elicitation_kani` | Kani proof harnesses for all verified operations |
| `elicitation_creusot` | Creusot deductive proofs |
| `elicitation_verus` | Verus SMT proofs |
| `elicitation_rand` | Randomised value generation for property testing |
| `elicit_reqwest` | HTTP workflow vocabulary |
| `elicit_sqlx` | Database workflow vocabulary |
| `elicit_tokio` | Async runtime vocabulary |
| `elicit_clap` | CLI vocabulary |
| `elicit_chrono` / `elicit_jiff` / `elicit_time` | Datetime vocabularies |
| `elicit_url` / `elicit_regex` / `elicit_uuid` | String-type vocabularies |
| `elicit_serde` / `elicit_serde_json` | Serialization vocabulary |
| `elicit_server` | MCP server support |
| `elicit_std` | Stdlib vocabulary |

---

## Further Reading

| Document | Topic |
|---|---|
| [`SHADOW_CRATE_MOTIVATION.md`]SHADOW_CRATE_MOTIVATION.md | The deep rationale for the inversion thesis |
| [`THIRD_PARTY_SUPPORT_GUIDE.md`]THIRD_PARTY_SUPPORT_GUIDE.md | How to add a new shadow crate end-to-end |
| [`ELICITATION_WORKFLOW_ARCHITECTURE.md`]ELICITATION_WORKFLOW_ARCHITECTURE.md | Workflow infrastructure deep dive |
| [`CREUSOT_GUIDE.md`]CREUSOT_GUIDE.md | Creusot annotation patterns |
| [`FORMAL_VERIFICATION_LEGOS.md`]FORMAL_VERIFICATION_LEGOS.md | Compositional proof strategy |
| [`crates/elicit_clap/`]crates/elicit_clap/ | Canonical reference shadow crate implementation |