Skip to main content
Tutorials4 min read

Recording VCR Cassettes for Package Verification

Step-by-step guide to recording HTTP cassettes for API-based tool packs. Record once locally, replay forever in the sandbox.

By agentnode

If your tool pack calls an external API, the verification sandbox will block those calls — network access is disabled by default. To prove your tool works anyway, you record the API responses once and the pipeline replays them on every verification run.

This guide walks you through recording a VCR cassette from scratch.

Prerequisites

  • Your tool pack uses httpx or requests for HTTP calls
  • You have working API credentials locally
  • Python 3.10+ with vcrpy installed
pip install vcrpy

Step 1: Create the Recording Script

Create a file called record_fixtures.py in your pack root:

"""Record VCR cassettes for verification fixtures."""
import vcr
import os

# Import your tool's run function
from my_tool_pack.tool import run

# Configure VCR
my_vcr = vcr.VCR(
    cassette_library_dir="fixtures/cassettes",
    record_mode="new_episodes",
    match_on=["method", "uri"],  # Don't match on body (multipart varies)
)

os.makedirs("fixtures/cassettes", exist_ok=True)

# Record each fixture case
with my_vcr.use_cassette("search_python.yaml"):
    result = run(query="Python programming", max_results=3)
    print(f"Recorded: {len(result.get('results', []))} results")

print("Done. Cassettes saved to fixtures/cassettes/")

Step 2: Run the Recording

cd my-tool-pack
python record_fixtures.py

This executes your tool with real API credentials and saves the HTTP interaction (request + response) to a YAML file.

Step 3: Inspect the Cassette

Open fixtures/cassettes/search_python.yaml — it looks like this:

interactions:
- request:
    body: q=Python+programming
    headers:
      User-Agent:
      - MyTool/1.0
    method: POST
    uri: https://api.example.com/search
  response:
    body:
      string: '{"results": [{"title": "Python.org", "url": "..."}]}'
    headers:
      Content-Type:
      - application/json
    status:
      code: 200
      message: OK
version: 1

Important: Check the cassette for leaked credentials. VCR records headers by default. If your API key was in a header, you'll see it in the YAML. Either:

  • Use filter_headers=['Authorization'] in your VCR config, or
  • Manually redact the cassette after recording

Step 4: Add to Your Manifest

verification:
  cases:
    - name: "search_python_programming"
      tool: "search_web"
      input:
        query: "Python programming"
        max_results: 3
      cassette: "fixtures/cassettes/search_python.yaml"
      expected:
        return_type: "dict"
        required_keys: ["results"]

The input must match exactly what you passed during recording. The pipeline will call your tool with these arguments while VCR intercepts and replays the saved response.

Step 5: Include Fixtures in Your Artifact

Create a MANIFEST.in in your pack root:

recursive-include fixtures *
include agentnode.yaml

This ensures python -m build --sdist includes your cassettes and manifest in the tarball.

Step 6: Verify Locally (Optional)

You can test that the cassette replays correctly without network access:

"""Test cassette replay."""
import vcr
from my_tool_pack.tool import run

with vcr.VCR(record_mode="none").use_cassette("fixtures/cassettes/search_python.yaml"):
    result = run(query="Python programming", max_results=3)
    assert "results" in result
    print(f"Replay OK: {len(result['results'])} results")

record_mode='none' means VCR will raise an error if any unmatched HTTP request is attempted — exactly what the sandbox does.

Step 7: Publish

agentnode publish

The pipeline will:

  1. Extract your artifact into /workspace/
  2. Install your package
  3. Import your tool
  4. For each case with a cassette: activate VCR with record_mode='none', call your tool with the declared input, validate against expected
  5. Run stability checks (3x same input, measure determinism)

VCR Matching Rules

The pipeline uses match_on=['method', 'uri'] by default. This means:

Matches onDoesn't match on
HTTP method (GET, POST)Request body
Full URI (scheme + host + path + query)Headers

This is important for multipart uploads (like file-based APIs) where the body contains boundary strings that change every time. As long as the method and URI match, the cassette replays.

Multiple Cassettes

You can have multiple cases, each with their own cassette:

verification:
  cases:
    - name: "search_python"
      input: {query: "Python"}
      cassette: "fixtures/cassettes/search_python.yaml"
    - name: "search_rust"
      input: {query: "Rust programming"}
      cassette: "fixtures/cassettes/search_rust.yaml"

Each cassette is independent. Record them separately in your script.

Common Pitfalls

Cassette path format

The validator enforces: fixtures/cassettes/<name>.yaml (or .yml or .json). No subdirectories, no absolute paths, no ...

Input mismatch

If the input in your manifest doesn't match what your tool sends as an HTTP request, VCR won't find a matching cassette entry and the test fails. Make sure the manifest input produces the exact same HTTP call as during recording.

API key routing

If your tool has an api_key parameter that switches between local and API mode, include it in the case input:

input:
  audio_path: "/workspace/fixtures/test.wav"
  api_key: "sk-fixture-test-key"

The key doesn't need to be real — VCR replays the response regardless. It just needs to trigger the API code path in your tool.

Summary

  1. Install vcrpy
  2. Write a recording script that calls your tool with real credentials
  3. Run it once to generate cassette files
  4. Check for leaked secrets, redact if needed
  5. Add verification.cases with cassette paths to your manifest
  6. Add MANIFEST.in to include fixtures
  7. Publish — the pipeline replays your cassettes in the sandbox

That's it. One recording session, permanent Gold eligibility.

#verification#tutorial#vcr#fixtures#api