Skip to content

Commit 28434a5

Browse files
committed
Upgrade MCP and add ask_user
1 parent 1e054ec commit 28434a5

File tree

4 files changed

+63
-1
lines changed

4 files changed

+63
-1
lines changed

docs/api/context.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,22 @@ result = await ctx.ask_llm(
4040
print(result.content.text)
4141
```
4242

43+
## User Elicitation
44+
45+
Call `ask_user()` when you need additional input from the client. It wraps the
46+
underlying MCP `elicit()` API:
47+
48+
```python
49+
class BookingPreferences(BaseModel):
50+
alternativeDate: str | None
51+
checkAlternative: bool = False
52+
53+
result = await ctx.ask_user(
54+
message="No tables available. Try another date?",
55+
schema=BookingPreferences,
56+
)
57+
```
58+
4359
## Extending Context
4460

4561
For now, if you need context functionality, you can extend the base class:

src/enrichmcp/context.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from typing import Literal
88

9+
from mcp.server.elicitation import ElicitationResult, ElicitSchemaModelT
910
from mcp.server.fastmcp import Context # pyright: ignore[reportMissingTypeArgument]
1011
from mcp.types import (
1112
CreateMessageResult,
@@ -107,6 +108,15 @@ async def sampling(
107108

108109
return await self.ask_llm(messages, **kwargs)
109110

111+
async def ask_user(
112+
self,
113+
message: str,
114+
schema: type[ElicitSchemaModelT],
115+
) -> ElicitationResult:
116+
"""Interactively ask the client for input using MCP elicitation."""
117+
118+
return await super().elicit(message=message, schema=schema)
119+
110120

111121
def prefer_fast_model() -> ModelPreferences:
112122
"""Model preferences optimized for speed and cost."""

tests/test_elicitation.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from unittest.mock import AsyncMock, Mock, patch
2+
3+
import pytest
4+
from pydantic import BaseModel
5+
6+
from enrichmcp import EnrichContext
7+
8+
9+
class Prefs(BaseModel):
10+
choice: bool
11+
12+
13+
@pytest.mark.asyncio
14+
async def test_ask_user_delegates_to_context_elicit():
15+
ctx = EnrichContext.model_construct(_request_context=Mock())
16+
17+
with patch("enrichmcp.context.Context.elicit", AsyncMock(return_value="ok")) as mock:
18+
got = await ctx.ask_user("hi", Prefs)
19+
assert got == "ok"
20+
mock.assert_awaited_once_with(message="hi", schema=Prefs)
21+
22+
23+
@pytest.mark.asyncio
24+
async def test_ask_user_requires_request_context():
25+
ctx = EnrichContext()
26+
27+
with pytest.raises(ValueError, match="outside of a request"):
28+
await ctx.ask_user("hi", Prefs)

uv.lock

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)