Skip to content

Commit db5ca59

Browse files
authored
Merge pull request #17 from modelcontextprotocol/davidsp/workflows
Github workflows for ruff and pyright
2 parents 9475815 + 211b5f0 commit db5ca59

20 files changed

+823
-107
lines changed

Diff for: .github/workflows/check-format.yml

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: ruff
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
format:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- uses: actions/checkout@v4
14+
15+
- name: Install uv
16+
uses: astral-sh/setup-uv@v3
17+
with:
18+
enable-cache: true
19+
20+
- name: "Set up Python"
21+
uses: actions/setup-python@v5
22+
with:
23+
python-version-file: ".python-version"
24+
25+
- name: Install the project
26+
run: uv sync --frozen --all-extras --dev
27+
28+
- name: Run ruff format check
29+
run: uv run --frozen ruff check .

Diff for: .github/workflows/check-types.yml

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: typecheck
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
typecheck:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- uses: actions/checkout@v4
14+
15+
- name: Install uv
16+
uses: astral-sh/setup-uv@v3
17+
with:
18+
enable-cache: true
19+
20+
- name: "Set up Python"
21+
uses: actions/setup-python@v5
22+
with:
23+
python-version-file: ".python-version"
24+
25+
- name: Install the project
26+
run: uv sync --frozen --all-extras --dev
27+
28+
- name: Run pyright
29+
run: uv run --frozen pyright

Diff for: .github/workflows/main.yml

+14-5
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,19 @@ jobs:
1111

1212
steps:
1313
- uses: actions/checkout@v4
14-
- uses: actions/setup-python@v5
14+
15+
- name: Install uv
16+
uses: astral-sh/setup-uv@v3
17+
with:
18+
enable-cache: true
19+
20+
- name: "Set up Python"
21+
uses: actions/setup-python@v5
1522
with:
16-
python-version: "3.10"
23+
python-version-file: ".python-version"
24+
25+
- name: Install the project
26+
run: uv sync --frozen --all-extras --dev
1727

18-
- run: pip install .
19-
- run: pip install -U pytest trio
20-
- run: pytest
28+
- name: Run pytest
29+
run: uv run --frozen pytest

Diff for: mcp_python/__init__.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
ReadResourceResult,
3838
Resource,
3939
ResourceUpdatedNotification,
40-
Role as SamplingRole,
4140
SamplingMessage,
4241
ServerCapabilities,
4342
ServerNotification,
@@ -49,6 +48,9 @@
4948
Tool,
5049
UnsubscribeRequest,
5150
)
51+
from .types import (
52+
Role as SamplingRole,
53+
)
5254

5355
__all__ = [
5456
"CallToolRequest",

Diff for: mcp_python/client/session.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ async def initialize(self) -> InitializeResult:
6262

6363
if result.protocolVersion != SUPPORTED_PROTOCOL_VERSION:
6464
raise RuntimeError(
65-
f"Unsupported protocol version from the server: {result.protocolVersion}"
65+
"Unsupported protocol version from the server: "
66+
f"{result.protocolVersion}"
6667
)
6768

6869
await self.send_notification(

Diff for: mcp_python/client/sse.py

+19-5
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,17 @@ def remove_request_params(url: str) -> str:
1919

2020

2121
@asynccontextmanager
22-
async def sse_client(url: str, headers: dict[str, Any] | None = None, timeout: float = 5, sse_read_timeout: float = 60 * 5):
22+
async def sse_client(
23+
url: str,
24+
headers: dict[str, Any] | None = None,
25+
timeout: float = 5,
26+
sse_read_timeout: float = 60 * 5,
27+
):
2328
"""
2429
Client transport for SSE.
2530
26-
`sse_read_timeout` determines how long (in seconds) the client will wait for a new event before disconnecting. All other HTTP operations are controlled by `timeout`.
31+
`sse_read_timeout` determines how long (in seconds) the client will wait for a new
32+
event before disconnecting. All other HTTP operations are controlled by `timeout`.
2733
"""
2834
read_stream: MemoryObjectReceiveStream[JSONRPCMessage | Exception]
2935
read_stream_writer: MemoryObjectSendStream[JSONRPCMessage | Exception]
@@ -67,7 +73,10 @@ async def sse_reader(
6773
or url_parsed.scheme
6874
!= endpoint_parsed.scheme
6975
):
70-
error_msg = f"Endpoint origin does not match connection origin: {endpoint_url}"
76+
error_msg = (
77+
"Endpoint origin does not match "
78+
f"connection origin: {endpoint_url}"
79+
)
7180
logger.error(error_msg)
7281
raise ValueError(error_msg)
7382

@@ -104,11 +113,16 @@ async def post_writer(endpoint_url: str):
104113
logger.debug(f"Sending client message: {message}")
105114
response = await client.post(
106115
endpoint_url,
107-
json=message.model_dump(by_alias=True, mode="json", exclude_none=True),
116+
json=message.model_dump(
117+
by_alias=True,
118+
mode="json",
119+
exclude_none=True,
120+
),
108121
)
109122
response.raise_for_status()
110123
logger.debug(
111-
f"Client message sent successfully: {response.status_code}"
124+
"Client message sent successfully: "
125+
f"{response.status_code}"
112126
)
113127
except Exception as exc:
114128
logger.error(f"Error in post_writer: {exc}")

Diff for: mcp_python/client/stdio.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ class StdioServerParameters(BaseModel):
2828
@asynccontextmanager
2929
async def stdio_client(server: StdioServerParameters):
3030
"""
31-
Client transport for stdio: this will connect to a server by spawning a process and communicating with it over stdin/stdout.
31+
Client transport for stdio: this will connect to a server by spawning a
32+
process and communicating with it over stdin/stdout.
3233
"""
3334
read_stream: MemoryObjectReceiveStream[JSONRPCMessage | Exception]
3435
read_stream_writer: MemoryObjectSendStream[JSONRPCMessage | Exception]

Diff for: mcp_python/server/__init__.py

+33-24
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,11 @@ def __init__(self, name: str):
5555

5656
def create_initialization_options(self) -> types.InitializationOptions:
5757
"""Create initialization options from this server instance."""
58+
5859
def pkg_version(package: str) -> str:
5960
try:
6061
from importlib.metadata import version
62+
6163
return version(package)
6264
except Exception:
6365
return "unknown"
@@ -69,16 +71,17 @@ def pkg_version(package: str) -> str:
6971
)
7072

7173
def get_capabilities(self) -> ServerCapabilities:
72-
"""Convert existing handlers to a ServerCapabilities object."""
73-
def get_capability(req_type: type) -> dict[str, Any] | None:
74-
return {} if req_type in self.request_handlers else None
74+
"""Convert existing handlers to a ServerCapabilities object."""
7575

76-
return ServerCapabilities(
77-
prompts=get_capability(ListPromptsRequest),
78-
resources=get_capability(ListResourcesRequest),
79-
tools=get_capability(ListPromptsRequest),
80-
logging=get_capability(SetLevelRequest)
81-
)
76+
def get_capability(req_type: type) -> dict[str, Any] | None:
77+
return {} if req_type in self.request_handlers else None
78+
79+
return ServerCapabilities(
80+
prompts=get_capability(ListPromptsRequest),
81+
resources=get_capability(ListResourcesRequest),
82+
tools=get_capability(ListPromptsRequest),
83+
logging=get_capability(SetLevelRequest),
84+
)
8285

8386
@property
8487
def request_context(self) -> RequestContext:
@@ -87,7 +90,7 @@ def request_context(self) -> RequestContext:
8790

8891
def list_prompts(self):
8992
def decorator(func: Callable[[], Awaitable[list[Prompt]]]):
90-
logger.debug(f"Registering handler for PromptListRequest")
93+
logger.debug("Registering handler for PromptListRequest")
9194

9295
async def handler(_: Any):
9396
prompts = await func()
@@ -103,17 +106,19 @@ def get_prompt(self):
103106
GetPromptRequest,
104107
GetPromptResult,
105108
ImageContent,
106-
Role as Role,
107109
SamplingMessage,
108110
TextContent,
109111
)
112+
from mcp_python.types import (
113+
Role as Role,
114+
)
110115

111116
def decorator(
112117
func: Callable[
113118
[str, dict[str, str] | None], Awaitable[types.PromptResponse]
114119
],
115120
):
116-
logger.debug(f"Registering handler for GetPromptRequest")
121+
logger.debug("Registering handler for GetPromptRequest")
117122

118123
async def handler(req: GetPromptRequest):
119124
prompt_get = await func(req.params.name, req.params.arguments)
@@ -149,7 +154,7 @@ async def handler(req: GetPromptRequest):
149154

150155
def list_resources(self):
151156
def decorator(func: Callable[[], Awaitable[list[Resource]]]):
152-
logger.debug(f"Registering handler for ListResourcesRequest")
157+
logger.debug("Registering handler for ListResourcesRequest")
153158

154159
async def handler(_: Any):
155160
resources = await func()
@@ -169,7 +174,7 @@ def read_resource(self):
169174
)
170175

171176
def decorator(func: Callable[[AnyUrl], Awaitable[str | bytes]]):
172-
logger.debug(f"Registering handler for ReadResourceRequest")
177+
logger.debug("Registering handler for ReadResourceRequest")
173178

174179
async def handler(req: ReadResourceRequest):
175180
result = await func(req.params.uri)
@@ -204,7 +209,7 @@ def set_logging_level(self):
204209
from mcp_python.types import EmptyResult
205210

206211
def decorator(func: Callable[[LoggingLevel], Awaitable[None]]):
207-
logger.debug(f"Registering handler for SetLevelRequest")
212+
logger.debug("Registering handler for SetLevelRequest")
208213

209214
async def handler(req: SetLevelRequest):
210215
await func(req.params.level)
@@ -219,7 +224,7 @@ def subscribe_resource(self):
219224
from mcp_python.types import EmptyResult
220225

221226
def decorator(func: Callable[[AnyUrl], Awaitable[None]]):
222-
logger.debug(f"Registering handler for SubscribeRequest")
227+
logger.debug("Registering handler for SubscribeRequest")
223228

224229
async def handler(req: SubscribeRequest):
225230
await func(req.params.uri)
@@ -234,7 +239,7 @@ def unsubscribe_resource(self):
234239
from mcp_python.types import EmptyResult
235240

236241
def decorator(func: Callable[[AnyUrl], Awaitable[None]]):
237-
logger.debug(f"Registering handler for UnsubscribeRequest")
242+
logger.debug("Registering handler for UnsubscribeRequest")
238243

239244
async def handler(req: UnsubscribeRequest):
240245
await func(req.params.uri)
@@ -249,7 +254,7 @@ def call_tool(self):
249254
from mcp_python.types import CallToolResult
250255

251256
def decorator(func: Callable[..., Awaitable[Any]]):
252-
logger.debug(f"Registering handler for CallToolRequest")
257+
logger.debug("Registering handler for CallToolRequest")
253258

254259
async def handler(req: CallToolRequest):
255260
result = await func(req.params.name, **(req.params.arguments or {}))
@@ -264,7 +269,7 @@ def progress_notification(self):
264269
def decorator(
265270
func: Callable[[str | int, float, float | None], Awaitable[None]],
266271
):
267-
logger.debug(f"Registering handler for ProgressNotification")
272+
logger.debug("Registering handler for ProgressNotification")
268273

269274
async def handler(req: ProgressNotification):
270275
await func(
@@ -286,7 +291,7 @@ def decorator(
286291
Awaitable[Completion | None],
287292
],
288293
):
289-
logger.debug(f"Registering handler for CompleteRequest")
294+
logger.debug("Registering handler for CompleteRequest")
290295

291296
async def handler(req: CompleteRequest):
292297
completion = await func(req.params.ref, req.params.argument)
@@ -307,10 +312,12 @@ async def run(
307312
self,
308313
read_stream: MemoryObjectReceiveStream[JSONRPCMessage | Exception],
309314
write_stream: MemoryObjectSendStream[JSONRPCMessage],
310-
initialization_options: types.InitializationOptions
315+
initialization_options: types.InitializationOptions,
311316
):
312317
with warnings.catch_warnings(record=True) as w:
313-
async with ServerSession(read_stream, write_stream, initialization_options) as session:
318+
async with ServerSession(
319+
read_stream, write_stream, initialization_options
320+
) as session:
314321
async for message in session.incoming_messages:
315322
logger.debug(f"Received message: {message}")
316323

@@ -359,14 +366,16 @@ async def run(
359366

360367
handler = self.notification_handlers[type(notify)]
361368
logger.debug(
362-
f"Dispatching notification of type {type(notify).__name__}"
369+
f"Dispatching notification of type "
370+
f"{type(notify).__name__}"
363371
)
364372

365373
try:
366374
await handler(notify)
367375
except Exception as err:
368376
logger.error(
369-
f"Uncaught exception in notification handler: {err}"
377+
f"Uncaught exception in notification handler: "
378+
f"{err}"
370379
)
371380

372381
for warning in w:

Diff for: mcp_python/server/__main__.py

+15-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
import importlib.metadata
12
import logging
23
import sys
3-
import importlib.metadata
4+
45
import anyio
56

67
from mcp_python.server.session import ServerSession
7-
from mcp_python.server.types import InitializationOptions
88
from mcp_python.server.stdio import stdio_server
9+
from mcp_python.server.types import InitializationOptions
910
from mcp_python.types import ServerCapabilities
1011

1112
if not sys.warnoptions:
@@ -30,7 +31,18 @@ async def receive_loop(session: ServerSession):
3031
async def main():
3132
version = importlib.metadata.version("mcp_python")
3233
async with stdio_server() as (read_stream, write_stream):
33-
async with ServerSession(read_stream, write_stream, InitializationOptions(server_name="mcp_python", server_version=version, capabilities=ServerCapabilities())) as session, write_stream:
34+
async with (
35+
ServerSession(
36+
read_stream,
37+
write_stream,
38+
InitializationOptions(
39+
server_name="mcp_python",
40+
server_version=version,
41+
capabilities=ServerCapabilities(),
42+
),
43+
) as session,
44+
write_stream,
45+
):
3446
await receive_loop(session)
3547

3648

0 commit comments

Comments
 (0)