Skip to content

Commit 869d4d8

Browse files
committed
Merge remote-tracking branch 'origin/main' into structured-output-typing-dict
2 parents fb9f5d1 + 72ff38c commit 869d4d8

28 files changed

Lines changed: 168 additions & 183 deletions

‎.github/workflows/main.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ permissions:
1111

1212
jobs:
1313
checks:
14-
uses: ./.github/workflows/shared.yml
14+
uses: $/.github/workflows/shared.yml
1515

1616
all-green:
1717
if: always()

‎.github/workflows/publish-pypi.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
path: dist/
3939

4040
checks:
41-
uses: ./.github/workflows/shared.yml
41+
uses: $/.github/workflows/shared.yml
4242

4343
pypi-publish:
4444
name: Upload release to PyPI

‎.pre-commit-config.yaml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ repos:
66
hooks:
77
- id: end-of-file-fixer
88

9-
- repo: https://github.com/pre-commit/mirrors-prettier
10-
rev: v3.1.0
9+
- repo: https://github.com/rbubley/mirrors-prettier
10+
rev: v3.9.10
1111
hooks:
1212
- id: prettier
1313
types_or: [yaml, json5]

‎docs/advanced/low-level-server.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ asyncio.run(main())
6161
The same text the `@mcp.tool()` version produced. Two honest differences:
6262

6363
* `result.structured_content` is `None`. The high-level server wraps a `-> str` into `{"result": ...}` for you; here nobody builds what you didn't build.
64-
* `list_tools` returns the schema **you** typed, character for character. The high-level version had `"title": "Query"` on every property and a `"title": "search_booksArguments"` at the root: Pydantic artifacts. Down here, if it's on the wire, you put it there.
64+
* `list_tools` returns the schema **you** typed, character for character. Down here, if it's on the wire, you put it there.
6565

6666
In a test you skip uvicorn and the port: `Client(server)` takes a low-level `Server` in-process exactly like it takes an `MCPServer`, and **[Testing](../get-started/testing.md)** is that pattern.
6767

‎docs/client/index.md‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,10 @@ and `tool.input_schema` is the JSON Schema the server derived from the function'
7272
{
7373
"type": "object",
7474
"properties": {
75-
"query": {"title": "Query", "type": "string"},
76-
"limit": {"default": 10, "title": "Limit", "type": "integer"}
75+
"query": {"type": "string"},
76+
"limit": {"default": 10, "type": "integer"}
7777
},
78-
"required": ["query"],
79-
"title": "search_booksArguments"
78+
"required": ["query"]
8079
}
8180
```
8281

‎docs/handlers/context.md‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,9 @@ This is the part to internalise. Here is the input schema `tools/list` reports f
3030
{
3131
"type": "object",
3232
"properties": {
33-
"query": {"title": "Query", "type": "string"}
33+
"query": {"type": "string"}
3434
},
35-
"required": ["query"],
36-
"title": "search_booksArguments"
35+
"required": ["query"]
3736
}
3837
```
3938

‎docs/handlers/dependencies.md‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,9 @@ Here is the input schema `tools/list` reports for `reserve_book`:
2828
{
2929
"type": "object",
3030
"properties": {
31-
"title": {"title": "Title", "type": "string"}
31+
"title": {"type": "string"}
3232
},
33-
"required": ["title"],
34-
"title": "reserve_bookArguments"
33+
"required": ["title"]
3534
}
3635
```
3736

‎docs/handlers/lifespan.md‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,9 @@ Nothing new. `ctx` is a **Context** parameter, so the SDK injects it and it neve
3232
{
3333
"type": "object",
3434
"properties": {
35-
"genre": {"title": "Genre", "type": "string"}
35+
"genre": {"type": "string"}
3636
},
37-
"required": ["genre"],
38-
"title": "count_booksArguments"
37+
"required": ["genre"]
3938
}
4039
```
4140

‎docs/servers/structured-output.md‎

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,9 @@ Because of it, the tool the SDK sends during `tools/list` carries an `output_sch
1919
```json
2020
{
2121
"properties": {
22-
"result": {"title": "Result", "type": "integer"}
22+
"result": {"type": "integer"}
2323
},
2424
"required": ["result"],
25-
"title": "get_temperatureOutput",
2625
"type": "object"
2726
}
2827
```
@@ -59,9 +58,9 @@ Declare the shape as a Pydantic `BaseModel` and return an instance:
5958
```json
6059
{
6160
"properties": {
62-
"temperature": {"description": "Degrees Celsius.", "title": "Temperature", "type": "number"},
63-
"humidity": {"description": "Relative humidity, 0 to 1.", "title": "Humidity", "type": "number"},
64-
"conditions": {"title": "Conditions", "type": "string"}
61+
"temperature": {"description": "Degrees Celsius.", "type": "number"},
62+
"humidity": {"description": "Relative humidity, 0 to 1.", "type": "number"},
63+
"conditions": {"type": "string"}
6564
},
6665
"required": ["temperature", "humidity", "conditions"],
6766
"title": "WeatherData",
@@ -125,20 +124,19 @@ A `list[...]` isn't a JSON object either, so it gets the `{"result": ...}` wrapp
125124
"$defs": {
126125
"WeatherData": {
127126
"properties": {
128-
"temperature": {"title": "Temperature", "type": "number"},
129-
"humidity": {"title": "Humidity", "type": "number"},
130-
"conditions": {"title": "Conditions", "type": "string"}
127+
"temperature": {"type": "number"},
128+
"humidity": {"type": "number"},
129+
"conditions": {"type": "string"}
131130
},
132131
"required": ["temperature", "humidity", "conditions"],
133132
"title": "WeatherData",
134133
"type": "object"
135134
}
136135
},
137136
"properties": {
138-
"result": {"items": {"$ref": "#/$defs/WeatherData"}, "title": "Result", "type": "array"}
137+
"result": {"items": {"$ref": "#/$defs/WeatherData"}, "type": "array"}
139138
},
140139
"required": ["result"],
141-
"title": "get_forecastOutput",
142140
"type": "object"
143141
}
144142
```
@@ -158,7 +156,6 @@ Ask for a two-day forecast and `structured_content` is `{"result": [{...}, {...}
158156
```json
159157
{
160158
"additionalProperties": {"type": "number"},
161-
"title": "get_temperaturesDictOutput",
162159
"type": "object"
163160
}
164161
```
@@ -169,7 +166,7 @@ result.structured_content # {"London": 16.2, "Reykjavik": 4.4}
169166

170167
The keys must be `str`. A `dict[int, float]` can't be a JSON object, so it falls back to the `{"result": ...}` wrapper.
171168

172-
Dictionary results use Pydantic's `TypeAdapter` for validation and serialization. If you inspect a tool's `FuncMetadata.output_model`, it holds the dictionary type annotation with its schema title.
169+
Dictionary results use Pydantic's `TypeAdapter` for validation and serialization. If you inspect a tool's `FuncMetadata.output_model`, it holds the dictionary type annotation itself.
173170

174171
## Validation
175172

‎docs/servers/tools.md‎

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,14 @@ From those type hints the SDK generates a JSON Schema and sends it to the client
2424
{
2525
"type": "object",
2626
"properties": {
27-
"query": {"title": "Query", "type": "string"},
28-
"limit": {"title": "Limit", "type": "integer"}
27+
"query": {"type": "string"},
28+
"limit": {"type": "integer"}
2929
},
30-
"required": ["query", "limit"],
31-
"title": "search_booksArguments"
30+
"required": ["query", "limit"]
3231
}
3332
```
3433

35-
Both arguments are in `required` because neither has a default. You'll fix that in a moment. (The `title` keys are Pydantic artifacts; the properties, their types, and `required` are the contract.)
34+
Both arguments are in `required` because neither has a default. You'll fix that in a moment. There are no `title` keys: the ones Pydantic would derive from the class and field names only repeat them. A title you set yourself with `Field(title=...)` is kept.
3635

3736
There is no `$schema` key either: MCP treats a schema without one as **JSON Schema 2020-12**, which is what Pydantic generates, so there is nothing to choose until you write schemas by hand on the **[low-level Server](../advanced/low-level-server.md#the-dialect-is-json-schema-2020-12)**.
3837

@@ -79,11 +78,10 @@ The schema follows:
7978
{
8079
"type": "object",
8180
"properties": {
82-
"query": {"title": "Query", "type": "string"},
83-
"limit": {"default": 10, "title": "Limit", "type": "integer"}
81+
"query": {"type": "string"},
82+
"limit": {"default": 10, "type": "integer"}
8483
},
85-
"required": ["query"],
86-
"title": "search_booksArguments"
84+
"required": ["query"]
8785
}
8886
```
8987

0 commit comments

Comments
 (0)