Join us and get access to thousands of tutorials and a community of expert Pythonistas.
This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.
Fetching Resources
00:00
To fetch resources from your MCP server, you’re going to create the subcommand fetch. You’re going to fetch a resource by its URI, its unique resource identifier, and once you get the resource, you’re going to display it to the user.
00:14
To do this, open the file cli.py, and at the top, use the decorator mcp_cli.command to create a new command. The help text for this would say something like
00:29 fetch a resource by name. It takes two arguments. The first one, as any other subcommand you wrote up to now, is the server command, and the second one is the URI, the unique resource identifier for your resource.
00:44
And the command is a function called fetch that accepts the arguments you specified, both strings. It returns None, and it uses asyncio to defer to the _fetch coroutine that you have to define next.
01:01
Now the async def _fetch accepts the same arguments, the server command, and the URI,
01:11
and it returns None. It will then connect to the server through your MCP client, using it as an asynchronous context manager, and then it’s going to take the client, access the client session, and use the function read_resource.
01:31
read_resource doesn’t expect a string, it expects an instance of AnyUrl, which is a Pydantic model. Scroll up and between click and rich you’re going to import AnyUrl from pydantic.
01:44
Now you don’t need to add it as an explicit dependency, because this is a dependency of MCP. From pydantic you import AnyUrl, you can go back to the coroutine fetch or _fetch, and you can wrap the string URI with AnyUrl.
02:03
Now read_resource is actually a coroutine, so you need to await it, and you can save the result in the variable resource. Again, the result resource is of a very specific type, that’s defined by the MCP library.
02:19 Check the documentation for details on it, but what you likely want to do is go through each part in the contents of the resource,
02:26 and you’ll want to print each part. You can save this.
02:31
To test the subcommand fetch, start by writing uv run mcp-client ls mcp-data, and you will see all of the resources that you have available.
02:42
Copy the URI from the only resource that’s available, and then write uv run mcp-client fetch mcp-data, and then paste the URI. Once you press Enter, you give it a second, and then you should get a result back that includes the text from the file in question.
03:01 Good job fetching resources from this MCP server. In the next lesson, you’re going to learn how to call tools with arbitrary inputs.
Bartosz Zaczyński RP Team on Aug. 6, 2026
@Mrn Om, that’s right, and thanks for pointing it out.
The MCP Python SDK v2 changed the uri parameter on .read_resource() (along with .subscribe_resource() and .unsubscribe_resource()) to accept only str. The MCP specification schema defines URIs as plain strings without strict URL validation, so this also means relative paths like users/me are accepted now.
In the _fetch() coroutine you build at 01:31, that means you can drop the wrapper entirely:
# v1, as shown in the lesson
from pydantic import AnyUrl
resource = await session.read_resource(AnyUrl(uri))
# v2
resource = await session.read_resource(uri)
The from pydantic import AnyUrl import isn’t needed anymore either.
If you’d rather follow the course exactly as recorded, you can pin the v1 SDK for now:
$ uv add "mcp<2"
The rest of the team covered the v2 rewrite in MCP Gets Its Biggest Rewrite if you want the wider picture. I’ll flag this lesson for an update.
Become a Member to join the conversation.
Mrn Om on Aug. 4, 2026
In mcp 2.0
read_resourceexpectsstrand fails ifAnyUrltype is provided.