Skip to content

Commit beb62d8

Browse files
committed
good progress
1 parent c635e03 commit beb62d8

File tree

13 files changed

+321
-5
lines changed

13 files changed

+321
-5
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,48 @@
11
# Annotations
2+
3+
πŸ‘¨β€πŸ’Ό Users want to feel confident about what each tool in the system does before they use it. They want to know if a tool is safe, if it will change their data, or if it might reach out to the outside world. When tools are clearly labeled for their behavior, the app feels more transparent and trustworthy.
4+
5+
For example, if a tool is read-only, the UI can show a reassuring message. If a tool might delete data, the user should get a warning. If a tool is safe to run multiple times, that's useful to know too. These labels help users make informed decisions and reduce anxiety about using advanced features.
6+
7+
To support this, each tool should include an `annotations` property that describes its behavior. Here are some fun examples:
8+
9+
```ts
10+
{
11+
title: 'Summon Dragon',
12+
description: 'Summon a friendly dragon to your location',
13+
annotations: {
14+
destructiveHint: false,
15+
openWorldHint: true,
16+
},
17+
inputSchema: summonInputSchema,
18+
}
19+
```
20+
21+
```ts
22+
{
23+
title: 'Cast Invisibility',
24+
description: 'Make the user invisible for 5 minutes',
25+
annotations: {
26+
idempotentHint: true,
27+
openWorldHint: false,
28+
},
29+
inputSchema: {},
30+
}
31+
```
32+
33+
```ts
34+
{
35+
title: 'Delete Goblin',
36+
description: 'Remove a goblin from the dungeon',
37+
annotations: {
38+
destructiveHint: true,
39+
idempotentHint: true,
40+
openWorldHint: false,
41+
},
42+
inputSchema: goblinIdSchema,
43+
}
44+
```
45+
46+
πŸ“œ Please check [the table in the MCP docs](https://modelcontextprotocol.io/docs/concepts/tools#available-tool-annotations) for the default values and when each annotation is useful.
47+
48+
The goal is to make tool behavior obvious to both users and the UI. The more clearly this is communicated, the more empowered and comfortable users will feel using the mcp server's advanced features.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
# Annotations
2+
3+
πŸ‘¨β€πŸ’Ό Fantastic progress! We've successfully introduced annotations to our journaling tools. Now, each tool clearly communicates its intentβ€”whether it's read-only, destructive, idempotent, or open world. This means users and the system can better understand the impact of each action, leading to a safer and more predictable journaling experience. For example, deleting an entry is now marked as idempotent, while creating a tag is clearly non-destructive. These improvements help ensure that users can confidently manage their journal and tags, knowing exactly what each tool is designed to do.
4+
5+
Let's keep up the momentum as we continue to enhance the user experience!
Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,70 @@
11
# Structured Output
22

3-
{/* TODO: add a note that we only have instructions for adding structuredOutput to a few of the tools, but they can do more if they have more time and want the practice */}
3+
πŸ‘¨β€πŸ’Ό When users open EpicMe, they expect their memories, reflections, and daily adventures to be organized, discoverable, and (most importantly) reliable. Imagine a user writing a heartfelt entry about their day, only to have it saved as a jumbled scroll of text! That would be chaos in their personal archive.
4+
5+
To ensure every tool in EpicMe returns information that is both predictable and easy for the UI (and our friendly LLM assistants) to understand, we use **structured output**. This means every tool response follows a clear, machine-validated shapeβ€”no more wild guesses or mysterious blobs of data.
6+
7+
```ts filename=src/tools.ts nocopy lines=5,9,11,15
8+
{
9+
name: 'create_magical_creature',
10+
description: `Add a new magical creature to the user's stable.`,
11+
inputSchema: { /* ... */ },
12+
outputSchema: { creature: magicalCreatureSchema },
13+
},
14+
async (input) => {
15+
const creature = await agent.db.createCreature(input)
16+
const structuredContent = { creature }
17+
return {
18+
structuredContent,
19+
content: [
20+
createTextContent(`Creature "${creature.name}" added to your stable!`),
21+
createCreatureResourceLink(creature),
22+
createTextContent(structuredContent),
23+
],
24+
}
25+
}
26+
```
27+
28+
Here's what a structured output response looks like:
29+
30+
```json nocopy
31+
{
32+
"content": [
33+
{
34+
"type": "text",
35+
"text": "Creature \"Twinklehoof\" added to your stable!"
36+
},
37+
{
38+
"type": "resource_link",
39+
"uri": "epicme://creatures/42",
40+
"mimeType": "application/json"
41+
},
42+
{
43+
"type": "text",
44+
"text": "{\"creature\":{\"id\":42,\"name\":\"Twinklehoof\",\"species\":\"Unicorn\"}}"
45+
}
46+
],
47+
"structuredContent": {
48+
"creature": {
49+
"id": 42,
50+
"name": "Twinklehoof",
51+
"species": "Unicorn"
52+
}
53+
}
54+
}
55+
```
56+
57+
<callout-info>
58+
Note that the `structuredContent` is a JSON object that matches the
59+
`outputSchema` of the tool and we also have a text content that is a JSON
60+
string equal to the `structuredContent` object. This is a fallback for clients
61+
that don't support structured output and is validated by the MCP Inspector.
62+
</callout-info>
63+
64+
<callout-muted>
65+
πŸ“œ For more details on structured output and output schemas, see the [official
66+
MCP
67+
documentation](https://modelcontextprotocol.io/specification/2025-06-18/server/tools#structured-content).
68+
</callout-muted>
69+
70+
The goal: make every journaling action in EpicMe feel seamless, safe, and delightfulβ€”so users can focus on capturing their stories, not on deciphering cryptic responses.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
# Structured Output
2+
3+
πŸ‘¨β€πŸ’Ό Outstanding achievement! We've now brought structured output to our journaling tools, ensuring every action in EpicMe returns information in a clear, machine-validated format. This means users can trust that their memories, reflections, and daily adventures are always organized and reliably presentedβ€”no more cryptic or unpredictable responses. The UI can now display journal entries, tags, and magical creatures (πŸ˜‰) with confidence, knowing exactly what to expect from each tool. This upgrade not only makes the experience more seamless and delightful for our users, but also paves the way for smarter automation and safer interactions throughout the app.
4+
5+
With structured output in place, EpicMe is even better equipped to help users capture and revisit their stories, making every journaling moment feel effortless and secure. Let's keep building on this momentum!
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Advanced Tools
2+
3+
Awesome work. You now understand about annotations, structured output, and output schemas. Hopefully this enables you to take advantage of great client features when clients support these features.
Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,55 @@
1-
# Elicite Confirmation
1+
# Elicit Confirmation
22

3-
{/* TODO: put an example of elicitation here because it's a lot */}
3+
πŸ‘¨β€πŸ’Ό When users interact with our system, they sometimes initiate actions that have significant consequences (like deleting a tag or entry). To ensure users don't accidentally lose something important, we need to pause and ask for explicit confirmation before proceeding.
4+
5+
This is where **elicitation** comes in. Instead of making assumptions, our MCP server can make a structured elicitation request. This lets us ask the user for confirmation in a way that's both clear and robust, and ensures the server only proceeds if the user truly intends to take the action.
6+
7+
```js
8+
const result = await agent.server.server.elicitInput({
9+
message: `Are you sure you want to delete sandwich "The Big Dipper" (ID: 42)?`,
10+
requestedSchema: {
11+
type: 'object',
12+
properties: {
13+
confirmed: {
14+
type: 'boolean',
15+
description: 'Whether to confirm the action',
16+
},
17+
},
18+
},
19+
})
20+
const confirmed =
21+
result.action === 'accept' && result.content?.confirmed === true
22+
23+
if (!confirmed) {
24+
const structuredContent = { success: false, sandwich: existingSandwich }
25+
return {
26+
structuredContent,
27+
content: [
28+
{
29+
type: 'text',
30+
text: `Deleting sandwich "The Big Dipper" (ID: 42) rejected by the user.`,
31+
},
32+
{
33+
type: 'resource_link',
34+
uri: `bobby-sandys://sandwiches/${existingSandwich.id}`,
35+
name: existingSandwich.name,
36+
mimeType: 'application/json',
37+
},
38+
{
39+
type: 'text',
40+
text: JSON.stringify(structuredContent),
41+
},
42+
],
43+
}
44+
}
45+
```
46+
47+
<callout-success>
48+
If the user does not confirm, the system gracefully cancels the action and
49+
provides clear feedback (no sandwiches are harmed)!
50+
</callout-success>
51+
52+
This approach ensures users are always in control of important actions, and the system responds with empathy and clarity.
53+
54+
🐨 Kody will be there in <InlineFile file="src/tools.ts" /> to help you
55+
implement this.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,11 @@
11
# Elicite Confirmation
2+
3+
πŸ‘¨β€πŸ’Ό Fantastic progress! We've just introduced a robust confirmation step for destructive actions, such as deleting a tag. Now, whenever a user initiates an action that could have significant consequences, the system pauses and asks for explicit confirmation using a structured elicitation prompt. This ensures that users are always in control and never accidentally lose important data.
4+
5+
By leveraging the Model Context Protocol's elicitation feature, our app now provides a clear, user-friendly dialog that requests confirmation before proceeding. If the user decides not to confirm, the action is gracefully cancelled and the user receives immediate feedbackβ€”no tags are deleted unless the user truly intends it.
6+
7+
This enhancement significantly improves trust and safety in our workflow, making sure that every important action is intentional and reversible. It's a big win for user experience and peace of mind!
8+
9+
---
10+
11+
πŸ§β€β™€οΈ I'm going to be adding more utilities and introducing sampling features to make our app even smarter. Feel free to try it yourself or just <NextDiffLink>check out my changes</NextDiffLink> if you're interested.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Elicitation
2+
3+
Awesome job! You now know how to elicit input from the user while your tools are running, making your MCP server user experience that much better.
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,125 @@
11
# Elicitation
2+
3+
Modern AI applications often need to collect structured input from users at key moments in a workflow. **Elicitation** in the Model Context Protocol (MCP) standardizes this process, allowing servers to request information from users through the client, using a well-defined schema and a secure, user-friendly interaction model.
4+
5+
Elicitation enables interactive workflows like confirming destructive actions, collecting additional details, or guiding users through multi-step forms by letting the server pause and ask the user for exactly what it needs, in a structured way.
6+
7+
<callout-info>
8+
Elicitation requests are always initiated by the server (so you need a
9+
persistent connection), but the client controls the user experience. The
10+
protocol does not mandate a specific UI, so clients can present prompts as
11+
dialogs, forms, or any interface that fits their platform.
12+
</callout-info>
13+
14+
## User Interaction Model
15+
16+
When an MCP server needs more information, it sends an `elicitation/create` request to the client, specifying:
17+
18+
- A **message** to display to the user (e.g., "What is your preferred delivery window?")
19+
- A **requestedSchema** (using a restricted JSON Schema) describing the expected input (e.g., a string `deliveryWindow` field with enum options)
20+
21+
The client presents this to the user, who can:
22+
23+
- **Accept** (provide the requested data)
24+
- **Decline** (explicitly refuse)
25+
- **Cancel** (dismiss without a choice)
26+
27+
The server must handle all three outcomes appropriately.
28+
29+
<callout-success>
30+
Elicitation is designed for trust and safety. Servers **must not** use it to
31+
request sensitive information. Clients should always make it clear which
32+
server is requesting input, and users should be able to review, modify, or
33+
decline any request.
34+
</callout-success>
35+
36+
## Example: Pizza Order Customization
37+
38+
Imagine an online pizza ordering system. After a user selects their pizza and toppings, the server wants to know if they'd like to add a drink to their order. Instead of assuming or cluttering the main UI, the server uses elicitation:
39+
40+
1. The user submits their pizza order.
41+
2. The server sends an `elicitation/create` request:
42+
43+
```json
44+
{
45+
"jsonrpc": "2.0",
46+
"id": 1,
47+
"method": "elicitation/create",
48+
"params": {
49+
"message": "Would you like to add a drink to your order?",
50+
"requestedSchema": {
51+
"type": "object",
52+
"properties": {
53+
"drink": {
54+
"type": "string",
55+
"title": "Drink Selection",
56+
"description": "Choose a drink to add to your order",
57+
"enum": ["None", "Cola", "Lemonade", "Water"],
58+
"enumNames": ["No drink", "Cola", "Lemonade", "Water"]
59+
}
60+
},
61+
"required": ["drink"]
62+
}
63+
}
64+
}
65+
```
66+
67+
The client presents this to the user, who can:
68+
69+
- **Accept** (provide the requested data)
70+
- **Decline** (explicitly refuse)
71+
- **Cancel** (dismiss without a choice)
72+
73+
Here's what the response from the client might look like:
74+
75+
```json
76+
{
77+
"jsonrpc": "2.0",
78+
"id": 1,
79+
"result": {
80+
"action": "accept", // or "decline" or "cancel"
81+
"content": {
82+
"drink": "Cola"
83+
}
84+
}
85+
}
86+
```
87+
88+
If the user selects a drink, it's added to the order. If they choose "None" or decline, the order proceeds without a drink.
89+
90+
For more details, see the [MCP Elicitation Spec](https://modelcontextprotocol.io/specification/2025-06-18/client/elicitation).
91+
92+
## Sequence Diagram
93+
94+
```mermaid
95+
sequenceDiagram
96+
participant User
97+
participant HostApp
98+
participant LLM
99+
participant Client
100+
participant Server
101+
102+
User->>HostApp: Submit pizza order prompt
103+
HostApp->>LLM: Forward prompt
104+
LLM->>HostApp: Decide to call tool (pizza_order)
105+
HostApp->>Client: Submit tool call
106+
Client->>Server: Forward tool call
107+
Server->>Client: Elicitation request (add drink?)
108+
Client->>HostApp: Forward elicitation request
109+
HostApp->>User: Present elicitation prompt
110+
User->>HostApp: Make selection (accept/decline/cancel)
111+
HostApp->>Client: Forward selection
112+
Client->>Server: Forward selection
113+
```
114+
115+
## Recommended Practices
116+
117+
- Use clear, concise messages for elicitation prompts.
118+
- Design schemas with only the fields you truly need (avoid requesting sensitive info).
119+
- Always handle accept, decline, and cancel actions.
120+
121+
## References
122+
123+
- πŸ“œ [MCP Elicitation Spec](https://modelcontextprotocol.io/specification/2025-06-18/client/elicitation)
124+
- πŸ“œ [Elicitation Concepts](https://modelcontextprotocol.io/docs/concepts/elicitation)
125+
- πŸ“œ [MCP Elicitations: Standardizing Interactive AI Workflows](https://blog.fka.dev/blog/2025-01-15-mcp-elicitations-standardizing-interactive-ai-workflows/)

β€Žexercises/03.sampling/01.solution.simple/README.mdxβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
πŸ‘¨β€πŸ’Ό Great! Now we're wired up to ask the LLM to do stuff for us. Let's get to
44
that next.
55

6-
πŸ§β€β™‚οΈ I'm going to get things implemented for actually creating and applying the
6+
πŸ§β€β™€οΈ I'm going to get things implemented for actually creating and applying the
77
suggested tags, but you're going to have to do the work of asking the LLM to
88
suggest them!
99

0 commit comments

Comments
Β (0)