diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 000000000..80c3605ec
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,158 @@
+# Changelog
+
+## [1.3.0] - 2025-02-20
+
+### Breaking Changes
+
+- **Context API Changes**: The Context logging methods (info, debug, warning, error) are now async and must be awaited. ([#172](https://github.com/modelcontextprotocol/python-sdk/pull/172))
+- **Resource Response Format**: Standardized resource response format to return both content and MIME type. Method `read_resource()` now returns a tuple of `(content, mime_type)` instead of just content. ([#170](https://github.com/modelcontextprotocol/python-sdk/pull/170))
+
+### New Features
+
+#### Lifespan Support
+Added comprehensive server lifecycle management through the lifespan API:
+```python
+@dataclass
+class AppContext:
+    db: Database
+
+@asynccontextmanager
+async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
+    try:
+        await db.connect()
+        yield AppContext(db=db)
+    finally:
+        await db.disconnect()
+
+mcp = FastMCP("My App", lifespan=app_lifespan)
+
+@mcp.tool()
+def query_db(ctx: Context) -> str:
+    db = ctx.request_context.lifespan_context["db"]
+    return db.query()
+```
+([#203](https://github.com/modelcontextprotocol/python-sdk/pull/203))
+
+#### Async Resources
+Added support for async resource functions in FastMCP:
+```python
+@mcp.resource("users://{user_id}")
+async def get_user(user_id: str) -> str:
+    async with client.session() as session:
+        response = await session.get(f"/users/{user_id}")
+        return await response.text()
+```
+([#157](https://github.com/modelcontextprotocol/python-sdk/pull/157))
+
+#### Concurrent Request Handling
+Made message handling concurrent, allowing multiple requests to be processed simultaneously. ([#206](https://github.com/modelcontextprotocol/python-sdk/pull/206))
+
+#### Request Cancellation
+Added support for canceling in-flight requests and cleaning up resources. ([#167](https://github.com/modelcontextprotocol/python-sdk/pull/167))
+
+#### Server Instructions
+Added support for the `instructions` field in server initialization, allowing servers to provide usage guidance. ([#150](https://github.com/modelcontextprotocol/python-sdk/pull/150))
+
+### Bug Fixes
+
+- Fixed progress reporting for first tool call by correcting progress_token handling ([#176](https://github.com/modelcontextprotocol/python-sdk/pull/176))
+- Fixed server crash when using debug logging ([#158](https://github.com/modelcontextprotocol/python-sdk/pull/158))
+- Fixed resource template handling in FastMCP server ([#137](https://github.com/modelcontextprotocol/python-sdk/pull/137))
+- Fixed MIME type preservation in resource responses ([#170](https://github.com/modelcontextprotocol/python-sdk/pull/170))
+- Fixed documentation for environment variables in CLI commands ([#149](https://github.com/modelcontextprotocol/python-sdk/pull/149))
+- Fixed request ID preservation in JSON-RPC responses ([#205](https://github.com/modelcontextprotocol/python-sdk/pull/205))
+
+### Dependency Updates
+
+- Relaxed version constraints for better compatibility:
+  - `pydantic`: Changed from `>=2.10.1,<3.0.0` to `>=2.7.2,<3.0.0`
+  - `pydantic-settings`: Changed from `>=2.6.1` to `>=2.5.2`
+  - `uvicorn`: Changed from `>=0.30` to `>=0.23.1`
+  ([#180](https://github.com/modelcontextprotocol/python-sdk/pull/180))
+
+### Examples
+
+- Added a simple chatbot example client to demonstrate SDK usage ([#98](https://github.com/modelcontextprotocol/python-sdk/pull/98))
+
+### Client Improvements
+
+- Added client support for sampling, list roots, and ping requests ([#218](https://github.com/modelcontextprotocol/python-sdk/pull/218))
+- Added flexible type system for tool result returns ([#222](https://github.com/modelcontextprotocol/python-sdk/pull/222))
+
+### Compatibility and Platform Support
+
+- Updated URL validation to allow file and other nonstandard schemas ([#68fcf92](https://github.com/modelcontextprotocol/python-sdk/commit/68fcf92947f7d02d50340053a72a969d6bb70e1b))
+- Force stdin/stdout encoding to UTF-8 for cross-platform compatibility ([#d92ee8f](https://github.com/modelcontextprotocol/python-sdk/commit/d92ee8feaa5675efddd399f3e8ebe8ed976b84c2))
+
+### Internal Improvements
+
+- Improved type annotations for better IDE support ([#181](https://github.com/modelcontextprotocol/python-sdk/pull/181))
+- Added comprehensive tests for SSE transport ([#151](https://github.com/modelcontextprotocol/python-sdk/pull/151))
+- Updated types to match 2024-11-05 MCP schema ([#165](https://github.com/modelcontextprotocol/python-sdk/pull/165))
+- Refactored request and notification handling for better code organization ([#166](https://github.com/modelcontextprotocol/python-sdk/pull/166))
+
+## [1.2.1] - 2024-01-27
+
+### Added
+- Support for async resources
+- Example and test for parameter descriptions in FastMCP tools
+
+### Fixed
+- MCP install command with environment variables
+- Resource template handling in FastMCP server (#129)
+- Package in the generated MCP run config (#128)
+- FastMCP logger debug output
+- Handling of strings containing numbers in FastMCP (@sd2k, #142)
+
+### Changed
+- Refactored to use built-in typing.Annotated instead of typing_extensions
+- Updated uv.lock
+- Added .DS_Store to gitignore
+
+# MCP Python SDK v1.2.0rc1 Release Notes
+
+## Major Features
+
+### FastMCP Integration
+- Integrated [FastMCP](https://github.com/jlowin/fastmcp) as the recommended high-level server framework
+- Added new `mcp.server.fastmcp` module with simplified decorator-based API
+- Introduced `FastMCP` class for easier server creation and management
+- Added comprehensive documentation and examples for FastMCP usage
+
+### New CLI Package
+- Added new CLI package for improved developer experience
+- Introduced `mcp dev` command for local development and testing
+- Added `mcp install` command for Claude Desktop integration
+- Added `mcp run` command for direct server execution
+
+## Improvements
+
+### Documentation
+- Completely revamped README with new structure and examples
+- Added detailed sections on core concepts (Resources, Tools, Prompts)
+- Updated documentation to recommend FastMCP as primary API
+- Added sections on development workflow and deployment options
+- Improved example server documentation
+
+### Developer Experience
+- Added pre-commit hooks for code quality
+- Updated to Pydantic 2.10.0 for improved type checking
+- Added uvicorn as a dependency for better server capabilities
+
+## Bug Fixes
+- Fixed deprecation warnings in core components
+- Fixed Pydantic field handling for meta fields
+- Fixed type issues throughout the codebase
+- Fixed example server READMEs
+
+## Breaking Changes
+- Deprecated direct usage of `mcp.server` in favor of `mcp.server.fastmcp`
+- Updated import paths for FastMCP integration
+- Changed recommended installation to include CLI features (`pip install "mcp[cli]"`)
+
+## Contributors
+Special thanks to all contributors who made this release possible, including:
+- Jeremiah Lowin (FastMCP)
+- Oskar Raszkiewicz
+
+**Full Changelog**: https://github.com/modelcontextprotocol/python-sdk/compare/v1.1.2...v1.2.0rc1
diff --git a/README.md b/README.md
index 370b4f334..bdbc9bca5 100644
--- a/README.md
+++ b/README.md
@@ -476,9 +476,21 @@ server_params = StdioServerParameters(
     env=None # Optional environment variables
 )
 
+# Optional: create a sampling callback
+async def handle_sampling_message(message: types.CreateMessageRequestParams) -> types.CreateMessageResult:
+    return types.CreateMessageResult(
+        role="assistant",
+        content=types.TextContent(
+            type="text",
+            text="Hello, world! from model",
+        ),
+        model="gpt-3.5-turbo",
+        stopReason="endTurn",
+    )
+
 async def run():
     async with stdio_client(server_params) as (read, write):
-        async with ClientSession(read, write) as session:
+        async with ClientSession(read, write, sampling_callback=handle_sampling_message) as session:
             # Initialize the connection
             await session.initialize()
 
diff --git a/examples/servers/simple-resource/mcp_simple_resource/server.py b/examples/servers/simple-resource/mcp_simple_resource/server.py
index 11ba56927..0ec1d926a 100644
--- a/examples/servers/simple-resource/mcp_simple_resource/server.py
+++ b/examples/servers/simple-resource/mcp_simple_resource/server.py
@@ -2,7 +2,7 @@
 import click
 import mcp.types as types
 from mcp.server.lowlevel import Server
-from pydantic import AnyUrl
+from pydantic import FileUrl
 
 SAMPLE_RESOURCES = {
     "greeting": "Hello! This is a sample text resource.",
@@ -26,7 +26,7 @@ def main(port: int, transport: str) -> int:
     async def list_resources() -> list[types.Resource]:
         return [
             types.Resource(
-                uri=AnyUrl(f"file:///{name}.txt"),
+                uri=FileUrl(f"file:///{name}.txt"),
                 name=name,
                 description=f"A sample text resource named {name}",
                 mimeType="text/plain",
@@ -35,8 +35,7 @@ async def list_resources() -> list[types.Resource]:
         ]
 
     @app.read_resource()
-    async def read_resource(uri: AnyUrl) -> str | bytes:
-        assert uri.path is not None
+    async def read_resource(uri: FileUrl) -> str | bytes:
         name = uri.path.replace(".txt", "").lstrip("/")
 
         if name not in SAMPLE_RESOURCES:
diff --git a/pyproject.toml b/pyproject.toml
index 05494d85f..2a5558a42 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "mcp"
-version = "1.3.0.dev0"
+version = "1.3.0"
 description = "Model Context Protocol SDK"
 readme = "README.md"
 requires-python = ">=3.10"
diff --git a/src/mcp/client/session.py b/src/mcp/client/session.py
index 4858ede50..c1cc5b5fc 100644
--- a/src/mcp/client/session.py
+++ b/src/mcp/client/session.py
@@ -1,13 +1,51 @@
 from datetime import timedelta
+from typing import Any, Protocol
 
 from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
-from pydantic import AnyUrl
+from pydantic import AnyUrl, TypeAdapter
 
 import mcp.types as types
-from mcp.shared.session import BaseSession
+from mcp.shared.context import RequestContext
+from mcp.shared.session import BaseSession, RequestResponder
 from mcp.shared.version import SUPPORTED_PROTOCOL_VERSIONS
 
 
+class SamplingFnT(Protocol):
+    async def __call__(
+        self,
+        context: RequestContext["ClientSession", Any],
+        params: types.CreateMessageRequestParams,
+    ) -> types.CreateMessageResult | types.ErrorData: ...
+
+
+class ListRootsFnT(Protocol):
+    async def __call__(
+        self, context: RequestContext["ClientSession", Any]
+    ) -> types.ListRootsResult | types.ErrorData: ...
+
+
+async def _default_sampling_callback(
+    context: RequestContext["ClientSession", Any],
+    params: types.CreateMessageRequestParams,
+) -> types.CreateMessageResult | types.ErrorData:
+    return types.ErrorData(
+        code=types.INVALID_REQUEST,
+        message="Sampling not supported",
+    )
+
+
+async def _default_list_roots_callback(
+    context: RequestContext["ClientSession", Any],
+) -> types.ListRootsResult | types.ErrorData:
+    return types.ErrorData(
+        code=types.INVALID_REQUEST,
+        message="List roots not supported",
+    )
+
+
+ClientResponse = TypeAdapter(types.ClientResult | types.ErrorData)
+
+
 class ClientSession(
     BaseSession[
         types.ClientRequest,
@@ -22,6 +60,8 @@ def __init__(
         read_stream: MemoryObjectReceiveStream[types.JSONRPCMessage | Exception],
         write_stream: MemoryObjectSendStream[types.JSONRPCMessage],
         read_timeout_seconds: timedelta | None = None,
+        sampling_callback: SamplingFnT | None = None,
+        list_roots_callback: ListRootsFnT | None = None,
     ) -> None:
         super().__init__(
             read_stream,
@@ -30,8 +70,24 @@ def __init__(
             types.ServerNotification,
             read_timeout_seconds=read_timeout_seconds,
         )
+        self._sampling_callback = sampling_callback or _default_sampling_callback
+        self._list_roots_callback = list_roots_callback or _default_list_roots_callback
 
     async def initialize(self) -> types.InitializeResult:
+        sampling = (
+            types.SamplingCapability() if self._sampling_callback is not None else None
+        )
+        roots = (
+            types.RootsCapability(
+                # TODO: Should this be based on whether we
+                # _will_ send notifications, or only whether
+                # they're supported?
+                listChanged=True,
+            )
+            if self._list_roots_callback is not None
+            else None
+        )
+
         result = await self.send_request(
             types.ClientRequest(
                 types.InitializeRequest(
@@ -39,14 +95,9 @@ async def initialize(self) -> types.InitializeResult:
                     params=types.InitializeRequestParams(
                         protocolVersion=types.LATEST_PROTOCOL_VERSION,
                         capabilities=types.ClientCapabilities(
-                            sampling=None,
+                            sampling=sampling,
                             experimental=None,
-                            roots=types.RootsCapability(
-                                # TODO: Should this be based on whether we
-                                # _will_ send notifications, or only whether
-                                # they're supported?
-                                listChanged=True
-                            ),
+                            roots=roots,
                         ),
                         clientInfo=types.Implementation(name="mcp", version="0.1.0"),
                     ),
@@ -243,3 +294,32 @@ async def send_roots_list_changed(self) -> None:
                 )
             )
         )
+
+    async def _received_request(
+        self, responder: RequestResponder[types.ServerRequest, types.ClientResult]
+    ) -> None:
+        ctx = RequestContext[ClientSession, Any](
+            request_id=responder.request_id,
+            meta=responder.request_meta,
+            session=self,
+            lifespan_context=None,
+        )
+
+        match responder.request.root:
+            case types.CreateMessageRequest(params=params):
+                with responder:
+                    response = await self._sampling_callback(ctx, params)
+                    client_response = ClientResponse.validate_python(response)
+                    await responder.respond(client_response)
+
+            case types.ListRootsRequest():
+                with responder:
+                    response = await self._list_roots_callback(ctx)
+                    client_response = ClientResponse.validate_python(response)
+                    await responder.respond(client_response)
+
+            case types.PingRequest():
+                with responder:
+                    return await responder.respond(
+                        types.ClientResult(root=types.EmptyResult())
+                    )
diff --git a/src/mcp/server/fastmcp/server.py b/src/mcp/server/fastmcp/server.py
index 5ae30a5ca..122acebb4 100644
--- a/src/mcp/server/fastmcp/server.py
+++ b/src/mcp/server/fastmcp/server.py
@@ -3,7 +3,7 @@
 import inspect
 import json
 import re
-from collections.abc import AsyncIterator
+from collections.abc import AsyncIterator, Iterable
 from contextlib import (
     AbstractAsyncContextManager,
     asynccontextmanager,
@@ -34,6 +34,7 @@
 from mcp.server.lowlevel.server import (
     lifespan as default_lifespan,
 )
+from mcp.server.session import ServerSession
 from mcp.server.sse import SseServerTransport
 from mcp.server.stdio import stdio_server
 from mcp.shared.context import RequestContext
@@ -235,7 +236,7 @@ async def list_resource_templates(self) -> list[MCPResourceTemplate]:
             for template in templates
         ]
 
-    async def read_resource(self, uri: AnyUrl | str) -> ReadResourceContents:
+    async def read_resource(self, uri: AnyUrl | str) -> Iterable[ReadResourceContents]:
         """Read a resource by URI."""
 
         resource = await self._resource_manager.get_resource(uri)
@@ -244,7 +245,7 @@ async def read_resource(self, uri: AnyUrl | str) -> ReadResourceContents:
 
         try:
             content = await resource.read()
-            return ReadResourceContents(content=content, mime_type=resource.mime_type)
+            return [ReadResourceContents(content=content, mime_type=resource.mime_type)]
         except Exception as e:
             logger.error(f"Error reading resource {uri}: {e}")
             raise ResourceError(str(e))
@@ -597,7 +598,7 @@ def my_tool(x: int, ctx: Context) -> str:
     The context is optional - tools that don't need it can omit the parameter.
     """
 
-    _request_context: RequestContext | None
+    _request_context: RequestContext[ServerSession, Any] | None
     _fastmcp: FastMCP | None
 
     def __init__(
@@ -648,7 +649,7 @@ async def report_progress(
             progress_token=progress_token, progress=progress, total=total
         )
 
-    async def read_resource(self, uri: str | AnyUrl) -> ReadResourceContents:
+    async def read_resource(self, uri: str | AnyUrl) -> Iterable[ReadResourceContents]:
         """Read a resource by URI.
 
         Args:
diff --git a/src/mcp/server/fastmcp/utilities/func_metadata.py b/src/mcp/server/fastmcp/utilities/func_metadata.py
index cf93049e3..629580e18 100644
--- a/src/mcp/server/fastmcp/utilities/func_metadata.py
+++ b/src/mcp/server/fastmcp/utilities/func_metadata.py
@@ -88,7 +88,7 @@ def pre_parse_json(self, data: dict[str, Any]) -> dict[str, Any]:
                     pre_parsed = json.loads(data[field_name])
                 except json.JSONDecodeError:
                     continue  # Not JSON - skip
-                if isinstance(pre_parsed, str):
+                if isinstance(pre_parsed, (str, int, float)):
                     # This is likely that the raw value is e.g. `"hello"` which we
                     # Should really be parsed as '"hello"' in Python - but if we parse
                     # it as JSON it'll turn into just 'hello'. So we skip it.
diff --git a/src/mcp/server/lowlevel/server.py b/src/mcp/server/lowlevel/server.py
index c0008b329..25e943651 100644
--- a/src/mcp/server/lowlevel/server.py
+++ b/src/mcp/server/lowlevel/server.py
@@ -67,9 +67,9 @@ async def main():
 import contextvars
 import logging
 import warnings
-from collections.abc import Awaitable, Callable
+from collections.abc import Awaitable, Callable, Iterable
 from contextlib import AbstractAsyncContextManager, AsyncExitStack, asynccontextmanager
-from typing import Any, AsyncIterator, Generic, Sequence, TypeVar
+from typing import Any, AsyncIterator, Generic, TypeVar
 
 import anyio
 from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
@@ -279,7 +279,9 @@ async def handler(_: Any):
 
     def read_resource(self):
         def decorator(
-            func: Callable[[AnyUrl], Awaitable[str | bytes | ReadResourceContents]],
+            func: Callable[
+                [AnyUrl], Awaitable[str | bytes | Iterable[ReadResourceContents]]
+            ],
         ):
             logger.debug("Registering handler for ReadResourceRequest")
 
@@ -307,13 +309,22 @@ def create_content(data: str | bytes, mime_type: str | None):
                     case str() | bytes() as data:
                         warnings.warn(
                             "Returning str or bytes from read_resource is deprecated. "
-                            "Use ReadResourceContents instead.",
+                            "Use Iterable[ReadResourceContents] instead.",
                             DeprecationWarning,
                             stacklevel=2,
                         )
                         content = create_content(data, None)
-                    case ReadResourceContents() as contents:
-                        content = create_content(contents.content, contents.mime_type)
+                    case Iterable() as contents:
+                        contents_list = [
+                            create_content(content_item.content, content_item.mime_type)
+                            for content_item in contents
+                            if isinstance(content_item, ReadResourceContents)
+                        ]
+                        return types.ServerResult(
+                            types.ReadResourceResult(
+                                contents=contents_list,
+                            )
+                        )
                     case _:
                         raise ValueError(
                             f"Unexpected return type from read_resource: {type(result)}"
@@ -387,7 +398,7 @@ def decorator(
             func: Callable[
                 ...,
                 Awaitable[
-                    Sequence[
+                    Iterable[
                         types.TextContent | types.ImageContent | types.EmbeddedResource
                     ]
                 ],
diff --git a/src/mcp/server/stdio.py b/src/mcp/server/stdio.py
index d74d6bc4c..0e0e49129 100644
--- a/src/mcp/server/stdio.py
+++ b/src/mcp/server/stdio.py
@@ -20,6 +20,7 @@ async def run_server():
 
 import sys
 from contextlib import asynccontextmanager
+from io import TextIOWrapper
 
 import anyio
 import anyio.lowlevel
@@ -38,11 +39,13 @@ async def stdio_server(
     from the current process' stdin and writing to stdout.
     """
     # Purposely not using context managers for these, as we don't want to close
-    # standard process handles.
+    # standard process handles. Encoding of stdin/stdout as text streams on
+    # python is platform-dependent (Windows is particularly problematic), so we
+    # re-wrap the underlying binary stream to ensure UTF-8.
     if not stdin:
-        stdin = anyio.wrap_file(sys.stdin)
+        stdin = anyio.wrap_file(TextIOWrapper(sys.stdin.buffer, encoding="utf-8"))
     if not stdout:
-        stdout = anyio.wrap_file(sys.stdout)
+        stdout = anyio.wrap_file(TextIOWrapper(sys.stdout.buffer, encoding="utf-8"))
 
     read_stream: MemoryObjectReceiveStream[types.JSONRPCMessage | Exception]
     read_stream_writer: MemoryObjectSendStream[types.JSONRPCMessage | Exception]
diff --git a/src/mcp/shared/memory.py b/src/mcp/shared/memory.py
index 72549925b..ae6b0be53 100644
--- a/src/mcp/shared/memory.py
+++ b/src/mcp/shared/memory.py
@@ -9,7 +9,7 @@
 import anyio
 from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
 
-from mcp.client.session import ClientSession
+from mcp.client.session import ClientSession, ListRootsFnT, SamplingFnT
 from mcp.server import Server
 from mcp.types import JSONRPCMessage
 
@@ -54,6 +54,8 @@ async def create_client_server_memory_streams() -> (
 async def create_connected_server_and_client_session(
     server: Server,
     read_timeout_seconds: timedelta | None = None,
+    sampling_callback: SamplingFnT | None = None,
+    list_roots_callback: ListRootsFnT | None = None,
     raise_exceptions: bool = False,
 ) -> AsyncGenerator[ClientSession, None]:
     """Creates a ClientSession that is connected to a running MCP server."""
@@ -80,6 +82,8 @@ async def create_connected_server_and_client_session(
                     read_stream=client_read,
                     write_stream=client_write,
                     read_timeout_seconds=read_timeout_seconds,
+                    sampling_callback=sampling_callback,
+                    list_roots_callback=list_roots_callback,
                 ) as client_session:
                     await client_session.initialize()
                     yield client_session
diff --git a/src/mcp/types.py b/src/mcp/types.py
index d1157aa62..7d867bd3b 100644
--- a/src/mcp/types.py
+++ b/src/mcp/types.py
@@ -1,7 +1,15 @@
-from typing import Annotated, Any, Callable, Generic, Literal, TypeAlias, TypeVar
+from typing import (
+    Annotated,
+    Any,
+    Callable,
+    Generic,
+    Literal,
+    TypeAlias,
+    TypeVar,
+)
 
 from pydantic import BaseModel, ConfigDict, Field, FileUrl, RootModel
-from pydantic.networks import AnyUrl
+from pydantic.networks import AnyUrl, UrlConstraints
 
 """
 Model Context Protocol bindings for Python
@@ -353,7 +361,7 @@ class Annotations(BaseModel):
 class Resource(BaseModel):
     """A known resource that the server is capable of reading."""
 
-    uri: AnyUrl
+    uri: Annotated[AnyUrl, UrlConstraints(host_required=False)]
     """The URI of this resource."""
     name: str
     """A human-readable name for this resource."""
@@ -415,7 +423,7 @@ class ListResourceTemplatesResult(PaginatedResult):
 class ReadResourceRequestParams(RequestParams):
     """Parameters for reading a resource."""
 
-    uri: AnyUrl
+    uri: Annotated[AnyUrl, UrlConstraints(host_required=False)]
     """
     The URI of the resource to read. The URI can use any protocol; it is up to the
     server how to interpret it.
@@ -433,7 +441,7 @@ class ReadResourceRequest(Request):
 class ResourceContents(BaseModel):
     """The contents of a specific resource or sub-resource."""
 
-    uri: AnyUrl
+    uri: Annotated[AnyUrl, UrlConstraints(host_required=False)]
     """The URI of this resource."""
     mimeType: str | None = None
     """The MIME type of this resource, if known."""
@@ -476,7 +484,7 @@ class ResourceListChangedNotification(Notification):
 class SubscribeRequestParams(RequestParams):
     """Parameters for subscribing to a resource."""
 
-    uri: AnyUrl
+    uri: Annotated[AnyUrl, UrlConstraints(host_required=False)]
     """
     The URI of the resource to subscribe to. The URI can use any protocol; it is up to
     the server how to interpret it.
@@ -497,7 +505,7 @@ class SubscribeRequest(Request):
 class UnsubscribeRequestParams(RequestParams):
     """Parameters for unsubscribing from a resource."""
 
-    uri: AnyUrl
+    uri: Annotated[AnyUrl, UrlConstraints(host_required=False)]
     """The URI of the resource to unsubscribe from."""
     model_config = ConfigDict(extra="allow")
 
@@ -515,7 +523,7 @@ class UnsubscribeRequest(Request):
 class ResourceUpdatedNotificationParams(NotificationParams):
     """Parameters for resource update notifications."""
 
-    uri: AnyUrl
+    uri: Annotated[AnyUrl, UrlConstraints(host_required=False)]
     """
     The URI of the resource that has been updated. This might be a sub-resource of the
     one that the client actually subscribed to.
diff --git a/tests/client/test_list_roots_callback.py b/tests/client/test_list_roots_callback.py
new file mode 100644
index 000000000..384e76760
--- /dev/null
+++ b/tests/client/test_list_roots_callback.py
@@ -0,0 +1,70 @@
+import pytest
+from pydantic import FileUrl
+
+from mcp.client.session import ClientSession
+from mcp.server.fastmcp.server import Context
+from mcp.shared.context import RequestContext
+from mcp.shared.memory import (
+    create_connected_server_and_client_session as create_session,
+)
+from mcp.types import (
+    ListRootsResult,
+    Root,
+    TextContent,
+)
+
+
+@pytest.mark.anyio
+async def test_list_roots_callback():
+    from mcp.server.fastmcp import FastMCP
+
+    server = FastMCP("test")
+
+    callback_return = ListRootsResult(
+        roots=[
+            Root(
+                uri=FileUrl("file://users/fake/test"),
+                name="Test Root 1",
+            ),
+            Root(
+                uri=FileUrl("file://users/fake/test/2"),
+                name="Test Root 2",
+            ),
+        ]
+    )
+
+    async def list_roots_callback(
+        context: RequestContext[ClientSession, None],
+    ) -> ListRootsResult:
+        return callback_return
+
+    @server.tool("test_list_roots")
+    async def test_list_roots(context: Context, message: str):
+        roots = await context.session.list_roots()
+        assert roots == callback_return
+        return True
+
+    # Test with list_roots callback
+    async with create_session(
+        server._mcp_server, list_roots_callback=list_roots_callback
+    ) as client_session:
+        # Make a request to trigger sampling callback
+        result = await client_session.call_tool(
+            "test_list_roots", {"message": "test message"}
+        )
+        assert result.isError is False
+        assert isinstance(result.content[0], TextContent)
+        assert result.content[0].text == "true"
+
+    # Test without list_roots callback
+    async with create_session(server._mcp_server) as client_session:
+        # Make a request to trigger sampling callback
+        result = await client_session.call_tool(
+            "test_list_roots", {"message": "test message"}
+        )
+        assert result.isError is True
+        assert isinstance(result.content[0], TextContent)
+        assert (
+            result.content[0].text
+            == "Error executing tool test_list_roots: List roots not supported"
+        )
diff --git a/tests/client/test_sampling_callback.py b/tests/client/test_sampling_callback.py
new file mode 100644
index 000000000..ba586d4a8
--- /dev/null
+++ b/tests/client/test_sampling_callback.py
@@ -0,0 +1,73 @@
+import pytest
+
+from mcp.client.session import ClientSession
+from mcp.shared.context import RequestContext
+from mcp.shared.memory import (
+    create_connected_server_and_client_session as create_session,
+)
+from mcp.types import (
+    CreateMessageRequestParams,
+    CreateMessageResult,
+    SamplingMessage,
+    TextContent,
+)
+
+
+@pytest.mark.anyio
+async def test_sampling_callback():
+    from mcp.server.fastmcp import FastMCP
+
+    server = FastMCP("test")
+
+    callback_return = CreateMessageResult(
+        role="assistant",
+        content=TextContent(
+            type="text", text="This is a response from the sampling callback"
+        ),
+        model="test-model",
+        stopReason="endTurn",
+    )
+
+    async def sampling_callback(
+        context: RequestContext[ClientSession, None],
+        params: CreateMessageRequestParams,
+    ) -> CreateMessageResult:
+        return callback_return
+
+    @server.tool("test_sampling")
+    async def test_sampling_tool(message: str):
+        value = await server.get_context().session.create_message(
+            messages=[
+                SamplingMessage(
+                    role="user", content=TextContent(type="text", text=message)
+                )
+            ],
+            max_tokens=100,
+        )
+        assert value == callback_return
+        return True
+
+    # Test with sampling callback
+    async with create_session(
+        server._mcp_server, sampling_callback=sampling_callback
+    ) as client_session:
+        # Make a request to trigger sampling callback
+        result = await client_session.call_tool(
+            "test_sampling", {"message": "Test message for sampling"}
+        )
+        assert result.isError is False
+        assert isinstance(result.content[0], TextContent)
+        assert result.content[0].text == "true"
+
+    # Test without sampling callback
+    async with create_session(server._mcp_server) as client_session:
+        # Make a request to trigger sampling callback
+        result = await client_session.call_tool(
+            "test_sampling", {"message": "Test message for sampling"}
+        )
+        assert result.isError is True
+        assert isinstance(result.content[0], TextContent)
+        assert (
+            result.content[0].text
+            == "Error executing tool test_sampling: Sampling not supported"
+        )
diff --git a/tests/client/test_stdio.py b/tests/client/test_stdio.py
index 0bdec72d0..95747ffd1 100644
--- a/tests/client/test_stdio.py
+++ b/tests/client/test_stdio.py
@@ -1,12 +1,17 @@
+import shutil
+
 import pytest
 
 from mcp.client.stdio import StdioServerParameters, stdio_client
 from mcp.types import JSONRPCMessage, JSONRPCRequest, JSONRPCResponse
 
+tee: str = shutil.which("tee")  # type: ignore
+
 
 @pytest.mark.anyio
+@pytest.mark.skipif(tee is None, reason="could not find tee command")
 async def test_stdio_client():
-    server_parameters = StdioServerParameters(command="/usr/bin/tee")
+    server_parameters = StdioServerParameters(command=tee)
 
     async with stdio_client(server_parameters) as (read_stream, write_stream):
         # Test sending and receiving messages
diff --git a/tests/issues/test_141_resource_templates.py b/tests/issues/test_141_resource_templates.py
index d6526e9ff..3c17cd559 100644
--- a/tests/issues/test_141_resource_templates.py
+++ b/tests/issues/test_141_resource_templates.py
@@ -51,8 +51,10 @@ def get_user_profile_missing(user_id: str) -> str:
 
     # Verify valid template works
     result = await mcp.read_resource("resource://users/123/posts/456")
-    assert result.content == "Post 456 by user 123"
-    assert result.mime_type == "text/plain"
+    result_list = list(result)
+    assert len(result_list) == 1
+    assert result_list[0].content == "Post 456 by user 123"
+    assert result_list[0].mime_type == "text/plain"
 
     # Verify invalid parameters raise error
     with pytest.raises(ValueError, match="Unknown resource"):
diff --git a/tests/issues/test_152_resource_mime_type.py b/tests/issues/test_152_resource_mime_type.py
index 7a1b66067..1143195e5 100644
--- a/tests/issues/test_152_resource_mime_type.py
+++ b/tests/issues/test_152_resource_mime_type.py
@@ -99,11 +99,11 @@ async def handle_list_resources():
     @server.read_resource()
     async def handle_read_resource(uri: AnyUrl):
         if str(uri) == "test://image":
-            return ReadResourceContents(content=base64_string, mime_type="image/png")
+            return [ReadResourceContents(content=base64_string, mime_type="image/png")]
         elif str(uri) == "test://image_bytes":
-            return ReadResourceContents(
-                content=bytes(image_bytes), mime_type="image/png"
-            )
+            return [
+                ReadResourceContents(content=bytes(image_bytes), mime_type="image/png")
+            ]
         raise Exception(f"Resource not found: {uri}")
 
     # Test that resources are listed with correct mime type
diff --git a/tests/issues/test_188_concurrency.py b/tests/issues/test_188_concurrency.py
index a56c0d300..2aa6c49cb 100644
--- a/tests/issues/test_188_concurrency.py
+++ b/tests/issues/test_188_concurrency.py
@@ -1,4 +1,5 @@
 import anyio
+import pytest
 from pydantic import AnyUrl
 
 from mcp.server.fastmcp import FastMCP
@@ -10,6 +11,7 @@
 _resource_name = "slow://slow_resource"
 
 
+@pytest.mark.anyio
 async def test_messages_are_executed_concurrently():
     server = FastMCP("test")
 
diff --git a/tests/server/fastmcp/servers/test_file_server.py b/tests/server/fastmcp/servers/test_file_server.py
index edaaa1596..c51ecb253 100644
--- a/tests/server/fastmcp/servers/test_file_server.py
+++ b/tests/server/fastmcp/servers/test_file_server.py
@@ -88,7 +88,10 @@ async def test_list_resources(mcp: FastMCP):
 
 @pytest.mark.anyio
 async def test_read_resource_dir(mcp: FastMCP):
-    res = await mcp.read_resource("dir://test_dir")
+    res_iter = await mcp.read_resource("dir://test_dir")
+    res_list = list(res_iter)
+    assert len(res_list) == 1
+    res = res_list[0]
     assert res.mime_type == "text/plain"
 
     files = json.loads(res.content)
@@ -102,7 +105,10 @@ async def test_read_resource_dir(mcp: FastMCP):
 
 @pytest.mark.anyio
 async def test_read_resource_file(mcp: FastMCP):
-    res = await mcp.read_resource("file://test_dir/example.py")
+    res_iter = await mcp.read_resource("file://test_dir/example.py")
+    res_list = list(res_iter)
+    assert len(res_list) == 1
+    res = res_list[0]
     assert res.content == "print('hello world')"
 
 
@@ -119,5 +125,8 @@ async def test_delete_file_and_check_resources(mcp: FastMCP, test_dir: Path):
     await mcp.call_tool(
         "delete_file", arguments=dict(path=str(test_dir / "example.py"))
     )
-    res = await mcp.read_resource("file://test_dir/example.py")
+    res_iter = await mcp.read_resource("file://test_dir/example.py")
+    res_list = list(res_iter)
+    assert len(res_list) == 1
+    res = res_list[0]
     assert res.content == "File not found"
diff --git a/tests/server/fastmcp/test_func_metadata.py b/tests/server/fastmcp/test_func_metadata.py
index 6461648eb..b1828ffe9 100644
--- a/tests/server/fastmcp/test_func_metadata.py
+++ b/tests/server/fastmcp/test_func_metadata.py
@@ -399,3 +399,18 @@ def test_complex_function_json_schema():
         "title": "complex_arguments_fnArguments",
         "type": "object",
     }
+
+
+def test_str_vs_int():
+    """
+    Test that string values are kept as strings even when they contain numbers,
+    while numbers are parsed correctly.
+    """
+
+    def func_with_str_and_int(a: str, b: int):
+        return a
+
+    meta = func_metadata(func_with_str_and_int)
+    result = meta.pre_parse_json({"a": "123", "b": 123})
+    assert result["a"] == "123"
+    assert result["b"] == 123
diff --git a/tests/server/fastmcp/test_server.py b/tests/server/fastmcp/test_server.py
index d90e9939f..5d375ccc8 100644
--- a/tests/server/fastmcp/test_server.py
+++ b/tests/server/fastmcp/test_server.py
@@ -581,7 +581,10 @@ def test_resource() -> str:
 
         @mcp.tool()
         async def tool_with_resource(ctx: Context) -> str:
-            r = await ctx.read_resource("test://data")
+            r_iter = await ctx.read_resource("test://data")
+            r_list = list(r_iter)
+            assert len(r_list) == 1
+            r = r_list[0]
             return f"Read resource: {r.content} with mime type {r.mime_type}"
 
         async with client_session(mcp._mcp_server) as client:
diff --git a/tests/server/test_read_resource.py b/tests/server/test_read_resource.py
index de00bc3d1..469eef857 100644
--- a/tests/server/test_read_resource.py
+++ b/tests/server/test_read_resource.py
@@ -1,3 +1,4 @@
+from collections.abc import Iterable
 from pathlib import Path
 from tempfile import NamedTemporaryFile
 
@@ -26,8 +27,8 @@ async def test_read_resource_text(temp_file: Path):
     server = Server("test")
 
     @server.read_resource()
-    async def read_resource(uri: AnyUrl) -> ReadResourceContents:
-        return ReadResourceContents(content="Hello World", mime_type="text/plain")
+    async def read_resource(uri: AnyUrl) -> Iterable[ReadResourceContents]:
+        return [ReadResourceContents(content="Hello World", mime_type="text/plain")]
 
     # Get the handler directly from the server
     handler = server.request_handlers[types.ReadResourceRequest]
@@ -54,10 +55,12 @@ async def test_read_resource_binary(temp_file: Path):
     server = Server("test")
 
     @server.read_resource()
-    async def read_resource(uri: AnyUrl) -> ReadResourceContents:
-        return ReadResourceContents(
-            content=b"Hello World", mime_type="application/octet-stream"
-        )
+    async def read_resource(uri: AnyUrl) -> Iterable[ReadResourceContents]:
+        return [
+            ReadResourceContents(
+                content=b"Hello World", mime_type="application/octet-stream"
+            )
+        ]
 
     # Get the handler directly from the server
     handler = server.request_handlers[types.ReadResourceRequest]
@@ -83,11 +86,13 @@ async def test_read_resource_default_mime(temp_file: Path):
     server = Server("test")
 
     @server.read_resource()
-    async def read_resource(uri: AnyUrl) -> ReadResourceContents:
-        return ReadResourceContents(
-            content="Hello World",
-            # No mime_type specified, should default to text/plain
-        )
+    async def read_resource(uri: AnyUrl) -> Iterable[ReadResourceContents]:
+        return [
+            ReadResourceContents(
+                content="Hello World",
+                # No mime_type specified, should default to text/plain
+            )
+        ]
 
     # Get the handler directly from the server
     handler = server.request_handlers[types.ReadResourceRequest]
diff --git a/tests/test_examples.py b/tests/test_examples.py
index b097fafbf..793a0d0ed 100644
--- a/tests/test_examples.py
+++ b/tests/test_examples.py
@@ -68,5 +68,5 @@ async def test_desktop(monkeypatch):
         content = result.contents[0]
         assert isinstance(content, TextResourceContents)
         assert isinstance(content.text, str)
-        assert "/fake/path/file1.txt" in content.text
-        assert "/fake/path/file2.txt" in content.text
+        assert "/fake/path/file1.txt" in content.text.replace('\\\\','/')
+        assert "/fake/path/file2.txt" in content.text.replace('\\\\','/')
