Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit ae77772

Browse filesBrowse files
authored
Add strict mode to pyright (#315)
* Add strict mode to pyright * Apply UP rule * fix readme * More correct * Leave wrong Context for now * Add strict mode to pyright * Apply UP rule * fix readme * fix * ignore
1 parent 5a54d82 commit ae77772
Copy full SHA for ae77772
Expand file treeCollapse file tree

27 files changed

+194
-133
lines changed

‎README.md

Copy file name to clipboardExpand all lines: README.md
+32-29Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,33 +16,36 @@
1616
<!-- omit in toc -->
1717
## Table of Contents
1818

19-
- [Overview](#overview)
20-
- [Installation](#installation)
21-
- [Quickstart](#quickstart)
22-
- [What is MCP?](#what-is-mcp)
23-
- [Core Concepts](#core-concepts)
24-
- [Server](#server)
25-
- [Resources](#resources)
26-
- [Tools](#tools)
27-
- [Prompts](#prompts)
28-
- [Images](#images)
29-
- [Context](#context)
30-
- [Running Your Server](#running-your-server)
31-
- [Development Mode](#development-mode)
32-
- [Claude Desktop Integration](#claude-desktop-integration)
33-
- [Direct Execution](#direct-execution)
34-
- [Mounting to an Existing ASGI Server](#mounting-to-an-existing-asgi-server)
35-
- [Examples](#examples)
36-
- [Echo Server](#echo-server)
37-
- [SQLite Explorer](#sqlite-explorer)
38-
- [Advanced Usage](#advanced-usage)
39-
- [Low-Level Server](#low-level-server)
40-
- [Writing MCP Clients](#writing-mcp-clients)
41-
- [MCP Primitives](#mcp-primitives)
42-
- [Server Capabilities](#server-capabilities)
43-
- [Documentation](#documentation)
44-
- [Contributing](#contributing)
45-
- [License](#license)
19+
- [MCP Python SDK](#mcp-python-sdk)
20+
- [Overview](#overview)
21+
- [Installation](#installation)
22+
- [Adding MCP to your python project](#adding-mcp-to-your-python-project)
23+
- [Running the standalone MCP development tools](#running-the-standalone-mcp-development-tools)
24+
- [Quickstart](#quickstart)
25+
- [What is MCP?](#what-is-mcp)
26+
- [Core Concepts](#core-concepts)
27+
- [Server](#server)
28+
- [Resources](#resources)
29+
- [Tools](#tools)
30+
- [Prompts](#prompts)
31+
- [Images](#images)
32+
- [Context](#context)
33+
- [Running Your Server](#running-your-server)
34+
- [Development Mode](#development-mode)
35+
- [Claude Desktop Integration](#claude-desktop-integration)
36+
- [Direct Execution](#direct-execution)
37+
- [Mounting to an Existing ASGI Server](#mounting-to-an-existing-asgi-server)
38+
- [Examples](#examples)
39+
- [Echo Server](#echo-server)
40+
- [SQLite Explorer](#sqlite-explorer)
41+
- [Advanced Usage](#advanced-usage)
42+
- [Low-Level Server](#low-level-server)
43+
- [Writing MCP Clients](#writing-mcp-clients)
44+
- [MCP Primitives](#mcp-primitives)
45+
- [Server Capabilities](#server-capabilities)
46+
- [Documentation](#documentation)
47+
- [Contributing](#contributing)
48+
- [License](#license)
4649

4750
[pypi-badge]: https://img.shields.io/pypi/v/mcp.svg
4851
[pypi-url]: https://pypi.org/project/mcp/
@@ -143,8 +146,8 @@ The FastMCP server is your core interface to the MCP protocol. It handles connec
143146
```python
144147
# Add lifespan support for startup/shutdown with strong typing
145148
from contextlib import asynccontextmanager
149+
from collections.abc import AsyncIterator
146150
from dataclasses import dataclass
147-
from typing import AsyncIterator
148151

149152
from fake_database import Database # Replace with your actual DB type
150153

@@ -442,7 +445,7 @@ For more control, you can use the low-level server implementation directly. This
442445

443446
```python
444447
from contextlib import asynccontextmanager
445-
from typing import AsyncIterator
448+
from collections.abc import AsyncIterator
446449

447450
from fake_database import Database # Replace with your actual DB type
448451

‎pyproject.toml

Copy file name to clipboardExpand all lines: pyproject.toml
+3-5Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,11 @@ packages = ["src/mcp"]
7676
include = ["src/mcp", "tests"]
7777
venvPath = "."
7878
venv = ".venv"
79-
strict = [
80-
"src/mcp/server/fastmcp/tools/base.py",
81-
"src/mcp/client/*.py"
82-
]
79+
strict = ["src/mcp/**/*.py"]
80+
exclude = ["src/mcp/types.py"]
8381

8482
[tool.ruff.lint]
85-
select = ["E", "F", "I"]
83+
select = ["E", "F", "I", "UP"]
8684
ignore = []
8785

8886
[tool.ruff]

‎src/mcp/cli/claude.py

Copy file name to clipboardExpand all lines: src/mcp/cli/claude.py
+2-4Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import os
55
import sys
66
from pathlib import Path
7+
from typing import Any
78

89
from mcp.server.fastmcp.utilities.logging import get_logger
910

@@ -116,10 +117,7 @@ def update_claude_config(
116117
# Add fastmcp run command
117118
args.extend(["mcp", "run", file_spec])
118119

119-
server_config = {
120-
"command": "uv",
121-
"args": args,
122-
}
120+
server_config: dict[str, Any] = {"command": "uv", "args": args}
123121

124122
# Add environment variables if specified
125123
if env_vars:

‎src/mcp/client/websocket.py

Copy file name to clipboardExpand all lines: src/mcp/client/websocket.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import json
22
import logging
3+
from collections.abc import AsyncGenerator
34
from contextlib import asynccontextmanager
4-
from typing import AsyncGenerator
55

66
import anyio
77
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream

‎src/mcp/server/fastmcp/prompts/base.py

Copy file name to clipboardExpand all lines: src/mcp/server/fastmcp/prompts/base.py
+19-19Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
import inspect
44
import json
5-
from collections.abc import Callable
6-
from typing import Any, Awaitable, Literal, Sequence
5+
from collections.abc import Awaitable, Callable, Sequence
6+
from typing import Any, Literal
77

88
import pydantic_core
99
from pydantic import BaseModel, Field, TypeAdapter, validate_call
@@ -19,7 +19,7 @@ class Message(BaseModel):
1919
role: Literal["user", "assistant"]
2020
content: CONTENT_TYPES
2121

22-
def __init__(self, content: str | CONTENT_TYPES, **kwargs):
22+
def __init__(self, content: str | CONTENT_TYPES, **kwargs: Any):
2323
if isinstance(content, str):
2424
content = TextContent(type="text", text=content)
2525
super().__init__(content=content, **kwargs)
@@ -30,7 +30,7 @@ class UserMessage(Message):
3030

3131
role: Literal["user", "assistant"] = "user"
3232

33-
def __init__(self, content: str | CONTENT_TYPES, **kwargs):
33+
def __init__(self, content: str | CONTENT_TYPES, **kwargs: Any):
3434
super().__init__(content=content, **kwargs)
3535

3636

@@ -39,11 +39,13 @@ class AssistantMessage(Message):
3939

4040
role: Literal["user", "assistant"] = "assistant"
4141

42-
def __init__(self, content: str | CONTENT_TYPES, **kwargs):
42+
def __init__(self, content: str | CONTENT_TYPES, **kwargs: Any):
4343
super().__init__(content=content, **kwargs)
4444

4545

46-
message_validator = TypeAdapter(UserMessage | AssistantMessage)
46+
message_validator = TypeAdapter[UserMessage | AssistantMessage](
47+
UserMessage | AssistantMessage
48+
)
4749

4850
SyncPromptResult = (
4951
str | Message | dict[str, Any] | Sequence[str | Message | dict[str, Any]]
@@ -73,12 +75,12 @@ class Prompt(BaseModel):
7375
arguments: list[PromptArgument] | None = Field(
7476
None, description="Arguments that can be passed to the prompt"
7577
)
76-
fn: Callable = Field(exclude=True)
78+
fn: Callable[..., PromptResult | Awaitable[PromptResult]] = Field(exclude=True)
7779

7880
@classmethod
7981
def from_function(
8082
cls,
81-
fn: Callable[..., PromptResult],
83+
fn: Callable[..., PromptResult | Awaitable[PromptResult]],
8284
name: str | None = None,
8385
description: str | None = None,
8486
) -> "Prompt":
@@ -99,7 +101,7 @@ def from_function(
99101
parameters = TypeAdapter(fn).json_schema()
100102

101103
# Convert parameters to PromptArguments
102-
arguments = []
104+
arguments: list[PromptArgument] = []
103105
if "properties" in parameters:
104106
for param_name, param in parameters["properties"].items():
105107
required = param_name in parameters.get("required", [])
@@ -138,25 +140,23 @@ async def render(self, arguments: dict[str, Any] | None = None) -> list[Message]
138140
result = await result
139141

140142
# Validate messages
141-
if not isinstance(result, (list, tuple)):
143+
if not isinstance(result, list | tuple):
142144
result = [result]
143145

144146
# Convert result to messages
145-
messages = []
146-
for msg in result:
147+
messages: list[Message] = []
148+
for msg in result: # type: ignore[reportUnknownVariableType]
147149
try:
148150
if isinstance(msg, Message):
149151
messages.append(msg)
150152
elif isinstance(msg, dict):
151-
msg = message_validator.validate_python(msg)
152-
messages.append(msg)
153+
messages.append(message_validator.validate_python(msg))
153154
elif isinstance(msg, str):
154-
messages.append(
155-
UserMessage(content=TextContent(type="text", text=msg))
156-
)
155+
content = TextContent(type="text", text=msg)
156+
messages.append(UserMessage(content=content))
157157
else:
158-
msg = json.dumps(pydantic_core.to_jsonable_python(msg))
159-
messages.append(Message(role="user", content=msg))
158+
content = json.dumps(pydantic_core.to_jsonable_python(msg))
159+
messages.append(Message(role="user", content=content))
160160
except Exception:
161161
raise ValueError(
162162
f"Could not convert prompt result to message: {msg}"

‎src/mcp/server/fastmcp/resources/resource_manager.py

Copy file name to clipboardExpand all lines: src/mcp/server/fastmcp/resources/resource_manager.py
+3-2Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Resource manager functionality."""
22

3-
from typing import Callable
3+
from collections.abc import Callable
4+
from typing import Any
45

56
from pydantic import AnyUrl
67

@@ -47,7 +48,7 @@ def add_resource(self, resource: Resource) -> Resource:
4748

4849
def add_template(
4950
self,
50-
fn: Callable,
51+
fn: Callable[..., Any],
5152
uri_template: str,
5253
name: str | None = None,
5354
description: str | None = None,

‎src/mcp/server/fastmcp/resources/templates.py

Copy file name to clipboardExpand all lines: src/mcp/server/fastmcp/resources/templates.py
+10-5Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
"""Resource template functionality."""
22

3+
from __future__ import annotations
4+
35
import inspect
46
import re
5-
from typing import Any, Callable
7+
from collections.abc import Callable
8+
from typing import Any
69

710
from pydantic import BaseModel, Field, TypeAdapter, validate_call
811

@@ -20,18 +23,20 @@ class ResourceTemplate(BaseModel):
2023
mime_type: str = Field(
2124
default="text/plain", description="MIME type of the resource content"
2225
)
23-
fn: Callable = Field(exclude=True)
24-
parameters: dict = Field(description="JSON schema for function parameters")
26+
fn: Callable[..., Any] = Field(exclude=True)
27+
parameters: dict[str, Any] = Field(
28+
description="JSON schema for function parameters"
29+
)
2530

2631
@classmethod
2732
def from_function(
2833
cls,
29-
fn: Callable,
34+
fn: Callable[..., Any],
3035
uri_template: str,
3136
name: str | None = None,
3237
description: str | None = None,
3338
mime_type: str | None = None,
34-
) -> "ResourceTemplate":
39+
) -> ResourceTemplate:
3540
"""Create a template from a function."""
3641
func_name = name or fn.__name__
3742
if func_name == "<lambda>":

‎src/mcp/server/fastmcp/server.py

Copy file name to clipboardExpand all lines: src/mcp/server/fastmcp/server.py
+13-10Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
import inspect
66
import json
77
import re
8-
from collections.abc import AsyncIterator, Iterable
8+
from collections.abc import AsyncIterator, Callable, Iterable, Sequence
99
from contextlib import (
1010
AbstractAsyncContextManager,
1111
asynccontextmanager,
1212
)
1313
from itertools import chain
14-
from typing import Any, Callable, Generic, Literal, Sequence
14+
from typing import Any, Generic, Literal
1515

1616
import anyio
1717
import pydantic_core
@@ -20,6 +20,7 @@
2020
from pydantic.networks import AnyUrl
2121
from pydantic_settings import BaseSettings, SettingsConfigDict
2222
from starlette.applications import Starlette
23+
from starlette.requests import Request
2324
from starlette.routing import Mount, Route
2425

2526
from mcp.server.fastmcp.exceptions import ResourceError
@@ -88,13 +89,13 @@ class Settings(BaseSettings, Generic[LifespanResultT]):
8889
)
8990

9091
lifespan: (
91-
Callable[["FastMCP"], AbstractAsyncContextManager[LifespanResultT]] | None
92+
Callable[[FastMCP], AbstractAsyncContextManager[LifespanResultT]] | None
9293
) = Field(None, description="Lifespan context manager")
9394

9495

9596
def lifespan_wrapper(
9697
app: FastMCP,
97-
lifespan: Callable[["FastMCP"], AbstractAsyncContextManager[LifespanResultT]],
98+
lifespan: Callable[[FastMCP], AbstractAsyncContextManager[LifespanResultT]],
9899
) -> Callable[[MCPServer[LifespanResultT]], AbstractAsyncContextManager[object]]:
99100
@asynccontextmanager
100101
async def wrap(s: MCPServer[LifespanResultT]) -> AsyncIterator[object]:
@@ -179,7 +180,7 @@ async def list_tools(self) -> list[MCPTool]:
179180
for info in tools
180181
]
181182

182-
def get_context(self) -> "Context[ServerSession, object]":
183+
def get_context(self) -> Context[ServerSession, object]:
183184
"""
184185
Returns a Context object. Note that the context will only be valid
185186
during a request; outside a request, most methods will error.
@@ -478,9 +479,11 @@ def sse_app(self) -> Starlette:
478479
"""Return an instance of the SSE server app."""
479480
sse = SseServerTransport("/messages/")
480481

481-
async def handle_sse(request):
482+
async def handle_sse(request: Request) -> None:
482483
async with sse.connect_sse(
483-
request.scope, request.receive, request._send
484+
request.scope,
485+
request.receive,
486+
request._send, # type: ignore[reportPrivateUsage]
484487
) as streams:
485488
await self._mcp_server.run(
486489
streams[0],
@@ -535,14 +538,14 @@ def _convert_to_content(
535538
if result is None:
536539
return []
537540

538-
if isinstance(result, (TextContent, ImageContent, EmbeddedResource)):
541+
if isinstance(result, TextContent | ImageContent | EmbeddedResource):
539542
return [result]
540543

541544
if isinstance(result, Image):
542545
return [result.to_image_content()]
543546

544-
if isinstance(result, (list, tuple)):
545-
return list(chain.from_iterable(_convert_to_content(item) for item in result))
547+
if isinstance(result, list | tuple):
548+
return list(chain.from_iterable(_convert_to_content(item) for item in result)) # type: ignore[reportUnknownVariableType]
546549

547550
if not isinstance(result, str):
548551
try:

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.