Skip to content

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.

    Run the following command in PowerShell.

    Terminal window
    powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
  2. Reopen your terminal (or PowerShell), then verify the installation.

    Terminal window
    uv --version

    If a version such as uv 0.11.6 is displayed, you are all set.

  1. Create a project and move into its folder.

    Terminal window
    uv init jina-research-agent
    cd jina-research-agent
  2. Install the Claude SDK.

    Terminal window
    uv add anthropic

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.

  1. Create setup.py in the project folder and save the following content. Replace the Your Jina AI API key here part with your actual API key.

    setup.py
    import json
    from anthropic import Anthropic
    client = 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")
  2. Run the script.

    Terminal window
    uv run setup.py

    The IDs of the created resources are displayed as follows.

    vault: vlt_011Cci1zcUettnGhV6NQabv9
    credential: vcrd_012oihCmwZH95gkNgf4Pix3e
    environment: env_018fgtuvYobdrA98ty299UpD
    agent: agent_018ZmPdCappkAQ5g3nc444To
    Saved IDs to ids.json

Creating a session and interacting with the agent go into a script you use on every run.

  1. Create run.py in the project folder and save the following content.

    run.py
    import json
    from anthropic import Anthropic
    client = 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":
    break
    elif event.type == "session.status_terminated":
    break
    stream.close()
    print("\nDone")
  2. Run the script.

    Terminal window
    uv run run.py

    Following the tool calls, the answer is displayed in real time (this takes about a minute).

    session: sesn_01CCczZDALAHspEXUqx9rhFQ
    Message 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
  3. Running run.py again 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.

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.

  1. Create chat.py in the project folder and save the following content.

    chat.py
    import json
    from anthropic import Anthropic
    client = 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":
    break
    if 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":
    break
    elif event.type == "session.status_terminated":
    break
    stream.close()
    print()
  2. Run the script and ask follow-up questions. You can confirm that it remembers the previous exchange.

    Terminal window
    uv run chat.py
    session: sesn_01UDB8UgiKxoFr1myDyGfjf2
    Chat with the agent. Type exit to quit.
    You> Hello
    Hello.
    You> Do you remember the first message I sent?
    Yes, it was your greeting, "Hello."
    You> exit
  • 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 via ids.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