3. Create an Agent with the SDK
We will create an agent using Python and the Claude SDK (the anthropic package). We use uv for the Python runtime environment throughout.
1. Install uv
Section titled “1. Install uv”-
Install uv.
Run the following command in PowerShell.
Terminal window powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"Run the following command in the terminal.
Terminal window curl -LsSf https://astral.sh/uv/install.sh | sh -
Reopen your terminal (or PowerShell), then verify the installation.
Terminal window uv --versionIf a version such as
uv 0.11.6is displayed, you are all set.
2. Create a Project
Section titled “2. Create a Project”-
Create a project and move into its folder.
Terminal window uv init jina-research-agentcd jina-research-agent -
Install the Claude SDK.
Terminal window uv add anthropic
3. Create the Resources
Section titled “3. Create the Resources”The credential vault, environment, and agent are “create once, reuse forever” resources, so we put their creation in a dedicated script. The contents are the same as what we created through the UI in 1. Create an Agent in the Console, with “-sdk” appended to the names.
-
Create
setup.pyin the project folder and save the following content. Replace theYour Jina AI API key herepart with your actual API key.setup.py import jsonfrom anthropic import Anthropicclient = Anthropic()################################ 1. Create a credential vault###############################vault = client.beta.vaults.create(display_name="Vault-sdk")print(f"vault: {vault.id}")################################ 2. Add the Jina AI API key to the vault###############################credential = client.beta.vaults.credentials.create(vault_id=vault.id,display_name="Jina-ai-sdk",auth={"type": "static_bearer","mcp_server_url": "https://mcp.jina.ai/v1","token": "Your Jina AI API key here",},)print(f"credential: {credential.id}")################################ 3. Create an environment (allow network access to MCP servers)###############################environment = client.beta.environments.create(name="Jina-environment-sdk",config={"type": "cloud","networking": {"type": "limited", "allow_mcp_servers": True},},)print(f"environment: {environment.id}")################################ 4. Create an agent###############################agent = client.beta.agents.create(name="Jina Web Research Agent SDK",model={"id": "claude-haiku-4-5", "speed": "standard"},description="A demo agent for web search, page reading, and academic research using Jina AI's MCP server.",system="You are a research assistant that leverages Jina AI's search and reading tools. When you receive a question, use search_web (or search_arxiv or search_ssrn for academic topics) to find relevant sources, then use read_url or parallel_read_url to retrieve the full text of promising results. Do not rely on search result snippets alone. Combine information from multiple sources, cite the URLs you used, and clearly indicate any uncertainties or conflicting information. For efficiency, prefer the parallel_* tools when searching or reading multiple sources at once. If asked to summarize or extract facts from a specific page, read that page directly with read_url. Keep your answers concise and well organized, and base them on the retrieved content rather than prior knowledge.",mcp_servers=[{"name": "jina", "type": "url", "url": "https://mcp.jina.ai/v1"}],tools=[{"type": "mcp_toolset","mcp_server_name": "jina","default_config": {"enabled": True,"permission_policy": {"type": "always_allow"},},}],)print(f"agent: {agent.id}")################################ Save the created IDs to a file (used by run.py)###############################with open("ids.json", "w") as f:json.dump({"vault_id": vault.id,"environment_id": environment.id,"agent_id": agent.id,},f,indent=2,)print("Saved IDs to ids.json") -
Run the script.
Terminal window uv run setup.pyThe IDs of the created resources are displayed as follows.
vault: vlt_011Cci1zcUettnGhV6NQabv9credential: vcrd_012oihCmwZH95gkNgf4Pix3eenvironment: env_018fgtuvYobdrA98ty299UpDagent: agent_018ZmPdCappkAQ5g3nc444ToSaved IDs to ids.json
4. Invoke the Agent
Section titled “4. Invoke the Agent”Creating a session and interacting with the agent go into a script you use on every run.
-
Create
run.pyin the project folder and save the following content.run.py import jsonfrom anthropic import Anthropicclient = Anthropic()################################ Load the IDs saved by setup.py###############################with open("ids.json") as f:ids = json.load(f)################################ 1. Create a session (combining the agent, environment, and vault)###############################session = client.beta.sessions.create(agent=ids["agent_id"],environment_id=ids["environment_id"],vault_ids=[ids["vault_id"]],)print(f"session: {session.id}")################################ 2. Open the event stream first (so no events are missed)###############################stream = client.beta.sessions.events.stream(session_id=session.id)################################ 3. Send a message###############################client.beta.sessions.events.send(session.id,events=[{"type": "user.message","content": [{"type": "text", "text": "Search for the latest papers on RAG"}],}],)print("Message sent. Waiting for a response...\n")################################ 4. Display events as they are received###############################for event in stream:if event.type == "agent.mcp_tool_use":print(f"[Tool call: {event.name}]")elif event.type == "agent.message":for block in event.content:if block.type == "text":print(block.text)elif event.type == "session.status_idle":if event.stop_reason.type != "requires_action":breakelif event.type == "session.status_terminated":breakstream.close()print("\nDone") -
Run the script.
Terminal window uv run run.pyFollowing the tool calls, the answer is displayed in real time (this takes about a minute).
session: sesn_01CCczZDALAHspEXUqx9rhFQMessage sent. Waiting for a response...[Tool call: search_arxiv]I found the latest RAG-related papers. ...## Latest RAG-Related Papers (Published June 2026)1. **TA-RAG: Tone-Aware Retrieval-Augmented Generation for Peer** (2606.06794)- A lightweight framework that incorporates tone control into the RAG pipeline...Done -
Running
run.pyagain starts a new session with the same agent. Try changing the message content. You can also review the resources you created and the session exchanges from the corresponding menus in the console.
5. Continue the Conversation
Section titled “5. Continue the Conversation”run.py ends after a single exchange, but sessions are stateful, so if you keep sending messages to the same session, the conversation continues. Let’s build an interactive version.
-
Create
chat.pyin the project folder and save the following content.chat.py import jsonfrom anthropic import Anthropicclient = Anthropic()################################ Load the IDs saved by setup.py###############################with open("ids.json") as f:ids = json.load(f)################################ Create only one session at the start (keep sending to the same session to continue the conversation)###############################session = client.beta.sessions.create(agent=ids["agent_id"],environment_id=ids["environment_id"],vault_ids=[ids["vault_id"]],)print(f"session: {session.id}")print("Chat with the agent. Type exit to quit.\n")while True:user_input = input("You> ").strip()if user_input == "exit":breakif not user_input:continue################################ Open the stream, then send the message###############################stream = client.beta.sessions.events.stream(session_id=session.id)client.beta.sessions.events.send(session.id,events=[{"type": "user.message","content": [{"type": "text", "text": user_input}],}],)################################ Display events until the agent is waiting for input###############################for event in stream:if event.type == "agent.mcp_tool_use":print(f"[Tool call: {event.name}]")elif event.type == "agent.message":for block in event.content:if block.type == "text":print(block.text)elif event.type == "session.status_idle":if event.stop_reason.type != "requires_action":breakelif event.type == "session.status_terminated":breakstream.close()print() -
Run the script and ask follow-up questions. You can confirm that it remembers the previous exchange.
Terminal window uv run chat.pysession: sesn_01UDB8UgiKxoFr1myDyGfjf2Chat with the agent. Type exit to quit.You> HelloHello.You> Do you remember the first message I sent?Yes, it was your greeting, "Hello."You> exit
Summary
Section titled “Summary”- We built the same setup as the console and CLI, this time with the Python SDK.
- The basic pattern is to split resource creation (
setup.py) from execution (run.py) into separate scripts and hand off the IDs viaids.json. - For events, open the stream first, then send, and finish receiving on
session.status_idle. - Sessions are stateful, so simply keep sending to the same session to continue the conversation (
chat.py). No history management is needed on the client side.
Great work! Please share your thoughts so far in a post on X.
Share your thoughts on X