A short, practical recipe: take a meeting transcript, pass it to Claude through the Python SDK (claude-agent-sdk), and get back a summary plus action items. No tools, no file system access, just text in and structured text out.
Prerequisites
Before you start, make sure the following are in place. Each line includes a command to verify it.
Python 3.10 or newer
python3 --versionNode.js 18 or newer (the Python SDK drives the Claude Code CLI under the hood)
node --versionThe Claude Code CLI, installed globally
npm install -g @anthropic-ai/claude-code
claude --versionA project folder with a virtual environment
mkdir transcript-summarizer
cd transcript-summarizer
python3 -m venv .venv
source .venv/bin/activateThe SDK and async runner, installed
pip install claude-agent-sdk anyio
python3 -c "import claude_agent_sdk; print('sdk ready')"An Anthropic API key, exported to your shell (get one at https://console.anthropic.com under API Keys)
export ANTHROPIC_API_KEY="sk-ant-your-key-here"
printenv ANTHROPIC_API_KEYAt least one transcript to summarize (a plain text file works; create a test one now)
python3 -c "open('transcript.txt','w').write('Ana: We ship Friday. Marko: I will finish QA Thursday.')"Assumed knowledge: basic Python, running a script with python file.py, and a first look at async/await (every SDK call is asynchronous). No prior experience with the Claude Code SDK, MCP, or the CLI is needed.
If all seven checks pass, you are ready to run the examples below.
1. Setup
If you have not installed the SDK yet:
pip install claude-agent-sdk anyio
npm install -g @anthropic-ai/claude-code
export ANTHROPIC_API_KEY="sk-ant-your-key-here"2. Summarize a transcript passed as text
Here we embed the transcript directly in the prompt and ask for a summary and action items. We set allowed_tools=[] so Claude reasons over the text only and does not try to touch the file system. Create summarize.py:
import anyio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, TextBlock
TRANSCRIPT = """
Ana: Thanks for joining. We need the new billing page live before the July release.
Marko: I can finish the frontend by Wednesday, but I am blocked on the pricing API.
Ana: Ivan, can you get the pricing API endpoints ready by Tuesday?
Ivan: Yes, I will have them on staging Tuesday morning.
Ana: Good. Marko, once the API is up, wire it in and send QA a build.
Marko: Will do. I will also add tests for the discount logic.
Ana: Let us also schedule a review with finance next Monday.
"""
async def main():
prompt = f"""Summarize the meeting transcript below.
Return two sections:
1. Summary: three sentences maximum.
2. Action items: a bulleted list. For each item include the owner and the due date if it was mentioned.
Transcript:
{TRANSCRIPT}
"""
options = ClaudeAgentOptions(
system_prompt="You are a precise meeting assistant. Be concise and do not invent details.",
allowed_tools=[],
max_turns=1,
)
async for message in query(prompt=prompt, options=options):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(block.text)
anyio.run(main)Run it:
python summarize.py3. Read the transcript from a file
Usually the transcript lives in a file. Read it in Python, then pass the text. Save a transcript first:
python -c "open('transcript.txt','w').write('Ana: We ship Friday. Marko: I will finish QA Thursday.')"Then create summarize_file.py:
import anyio
from pathlib import Path
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, TextBlock
async def main():
transcript = Path("transcript.txt").read_text()
prompt = f"""Summarize this transcript in three sentences, then list action items with owners.
Transcript:
{transcript}
"""
options = ClaudeAgentOptions(allowed_tools=[], max_turns=1)
async for message in query(prompt=prompt, options=options):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(block.text)
anyio.run(main)python summarize_file.py4. Get action items as structured JSON
For anything downstream (a ticket tracker, a database, an email), ask for JSON and parse it. We read the final answer from the ResultMessage. Create summarize_json.py:
import anyio
import json
from pathlib import Path
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
async def main():
transcript = Path("transcript.txt").read_text()
prompt = f"""Read the transcript and return ONLY valid JSON, no markdown, in this exact shape:
{{
"summary": "short paragraph",
"action_items": [
{{"task": "string", "owner": "string or null", "due": "string or null"}}
]
}}
Transcript:
{transcript}
"""
options = ClaudeAgentOptions(allowed_tools=[], max_turns=1)
raw = ""
async for message in query(prompt=prompt, options=options):
if isinstance(message, ResultMessage):
raw = message.result or ""
data = json.loads(raw)
print("Summary:", data["summary"])
print("\nAction items:")
for item in data["action_items"]:
owner = item.get("owner") or "unassigned"
due = item.get("due") or "no date"
print(f"- {item['task']} ({owner}, {due})")
anyio.run(main)python summarize_json.py5. Summarize a folder of transcripts
Loop over multiple files and produce one JSON summary each. Create summarize_batch.py:
import anyio
import json
from pathlib import Path
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
async def summarize_one(path: Path):
transcript = path.read_text()
prompt = f"""Return ONLY JSON: {{"summary": "...", "action_items": ["..."]}}.
Transcript:
{transcript}
"""
options = ClaudeAgentOptions(allowed_tools=[], max_turns=1)
raw = ""
async for message in query(prompt=prompt, options=options):
if isinstance(message, ResultMessage):
raw = message.result or ""
return json.loads(raw)
async def main():
folder = Path("transcripts")
for path in sorted(folder.glob("*.txt")):
result = await summarize_one(path)
print(f"\n=== {path.name} ===")
print(result["summary"])
for item in result["action_items"]:
print(f"- {item}")
anyio.run(main)Put your .txt files in a transcripts/ folder, then run:
python summarize_batch.pyNotes
- If a transcript is very large, chunk it into sections, summarize each, then summarize the summaries. One prompt has a context limit.
- Keep
allowed_tools=[]for this task. You are not asking Claude to touch files or run commands, so there is no reason to grant tools. - If
json.loadsfails, tighten the prompt with “return ONLY valid JSON, no code fences” and strip any leading or trailing backticks before parsing.