10-Minute Quickstart
This guide gets you to a working context build, a firewall-protected tool result, and a routed tool shortlist in under 10 minutes.
In a hurry? After
pip install contextweaver, the built-in demo walks you through the same ideas in one command:
bash contextweaver demo # friendly walkthrough contextweaver demo --scenario large-catalog # 1,000 tools → compact cards contextweaver demo --scenario huge-tool-output # context firewall on a big tool result contextweaver demo --scenario mcp-gateway # MCP gateway meta-tools end-to-endEach scenario is deterministic and network-free. Run
contextweaver demo --helpto see the full list.
Time budget:
- Prerequisites: 30 seconds
- Install: 30 seconds
- Your first context build: 3 minutes
- Try the context firewall: 4 minutes
- Try tool routing: 2 minutes
- What to try next: 1 minute
Adopting from an existing chat history (5-line drop-in)
Already have an OpenAI, Anthropic, or Gemini agent? You don't need to walk the full quickstart — drop contextweaver in front of your existing message history with one call. The full walkthrough below is for new agents.
If you have an OpenAI Chat Completions session saved as JSON, you can build a context pack in five lines (plus imports):
import json
from contextweaver.adapters.openai_messages import from_openai_messages
from contextweaver.context.manager import ContextManager
from contextweaver.types import Phase
mgr = ContextManager()
from_openai_messages(json.load(open("session.json")), into=mgr)
pack = mgr.build_sync(phase=Phase.answer, query="What did we decide?")
print(pack.prompt)
The adapter handles every OpenAI Chat Completions role — system, user,
assistant (with optional tool_calls), tool — and threads
tool_call_id ↔ ContextItem.id so to_openai_messages(...) is the exact
inverse for round-tripping back into the OpenAI SDK.
Anthropic and Google Gemini have sibling adapters with the same shape:
from contextweaver.adapters.anthropic_messages import from_anthropic_messages
from contextweaver.adapters.gemini_contents import from_gemini_contents
# Anthropic Messages API: content blocks (text / tool_use / tool_result)
from_anthropic_messages(anthropic_messages, into=mgr)
# Google Gemini: contents[].parts[] (text / functionCall / functionResponse)
from_gemini_contents(gemini_contents, into=mgr)
All three adapters are pure stateless converters — they accept plain dicts
and never import a provider SDK at module load time. Session payloads may
contain sensitive prompt content; the adapters do not log message bodies
above DEBUG level.
Want to keep working with the provider SDK after building a pack? Each adapter ships an inverse:
to_openai_messages,to_anthropic_messages,to_gemini_contents. Round-trip equality holds for the representative fixtures intests/test_adapters_*.py.
1. Prerequisites (30 seconds)
contextweaver requires Python 3.10 or newer.
Check your Python version:
python --version
Create and activate a virtual environment:
python -m venv .venv
Linux and macOS:
source .venv/bin/activate
Windows PowerShell:
.venv\Scripts\Activate.ps1
If you see an error like running scripts is disabled on this system, either:
- run the activation script from Command Prompt (cmd.exe) instead:
cmd
.venv\Scripts\activate.bat
- or relax the execution policy for your current user in PowerShell (recommended only on machines you control):
powershell
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
2. Install (30 seconds)
Install from PyPI:
pip install contextweaver
If you are working from a repository checkout instead, install the package in editable mode:
pip install -e ".[dev]"
If your network blocks openaipublic.blob.core.windows.net, the demo or
token-budget helpers may print tiktoken cl100k_base encoding unavailable.
That warning is harmless: contextweaver falls back to the deterministic
chars/4 estimator and keeps enforcing budgets. To suppress it while keeping
exact tiktoken counts, pre-warm a TIKTOKEN_CACHE_DIR on a connected
machine and copy that cache into the offline environment; the
troubleshooting guide
has the full workflow.
3. Your First Context Build (3 minutes)
Scenario: an agent receives a question, decides to query a database, and builds an answer-phase prompt from the conversation history.
Save this as first_agent.py:
"""Your first contextweaver context build."""
from contextweaver.context.manager import ContextManager
from contextweaver.types import ContextItem, ItemKind, Phase
mgr = ContextManager()
mgr.ingest(ContextItem(id="u1", kind=ItemKind.user_turn, text="How many active users do we have?"))
mgr.ingest(ContextItem(id="a1", kind=ItemKind.agent_msg, text="I'll check the database for you."))
mgr.ingest(
ContextItem(
id="tc1",
kind=ItemKind.tool_call,
text='db_query(sql="SELECT COUNT(*) FROM users WHERE active=true")',
parent_id="u1",
)
)
mgr.ingest(ContextItem(id="tr1", kind=ItemKind.tool_result, text="count: 1042", parent_id="tc1"))
pack = mgr.build_sync(phase=Phase.answer, query="active user count")
print("=== Compiled Context ===")
print(pack.prompt)
print("\n=== Build Stats ===")
print(f"Total candidates: {pack.stats.total_candidates}")
print(f"Included in prompt: {pack.stats.included_count}")
print(f"Dropped: {pack.stats.dropped_count}")
print(f"Deduplicated: {pack.stats.dedup_removed}")
Run it:
python first_agent.py
Expected output excerpt:
=== Compiled Context ===
[TOOL RESULT [artifact:tr1]]
count: 1042
[TOOL CALL]
db_query(sql="SELECT COUNT(*) FROM users WHERE active=true")
[USER]
How many active users do we have?
[ASSISTANT]
I'll check the database for you.
=== Build Stats ===
Total candidates: 4
Included in prompt: 4
Dropped: 0
Deduplicated: 0
What just happened:
- You ingested four events into the event log.
build_sync()ran the context pipeline for theanswerphase.- The prompt was compiled from the most relevant items and returned with build stats.
Sensitivity & Default Drops
Every ContextItem has a sensitivity level. It defaults to public, and
the default policy is conservative: items at or above
Sensitivity.confidential are dropped before scoring and rendering.
from contextweaver.config import ContextPolicy
from contextweaver.context.manager import ContextManager
from contextweaver.types import ContextItem, ItemKind, Phase, Sensitivity
mgr = ContextManager()
mgr.ingest(ContextItem(id="u1", kind=ItemKind.user_turn, text="public"))
mgr.ingest(
ContextItem(
id="c1",
kind=ItemKind.user_turn,
text="confidential",
sensitivity=Sensitivity.confidential,
)
)
pack = mgr.build_sync(phase=Phase.answer, query="any")
print(pack.stats.dropped_reasons.get("sensitivity", 0)) # 1
If you want to keep confidential items but still drop restricted items,
raise the floor:
policy = ContextPolicy(sensitivity_floor=Sensitivity.restricted)
mgr = ContextManager(policy=policy)
If you want sensitive items to remain visible only as masks, use redact mode:
policy = ContextPolicy(sensitivity_action="redact")
mgr = ContextManager(policy=policy)
4. Try the Context Firewall (4 minutes)
Problem: a large tool result can dominate the prompt if you include it verbatim.
Save this as firewall_demo.py:
"""Show how the context firewall keeps prompts compact."""
from contextweaver.context.manager import ContextManager
from contextweaver.types import ContextItem, ItemKind, Phase
large_result = '{"users": [' + ', '.join(
[
f'{{"id": {i}, "name": "User{i}", "email": "user{i}@example.com"}}'
for i in range(1, 101)
]
) + ']}'
mgr = ContextManager()
mgr.ingest(ContextItem(id="u1", kind=ItemKind.user_turn, text="List all users"))
mgr.ingest(ContextItem(id="tc1", kind=ItemKind.tool_call, text="list_users()", parent_id="u1"))
mgr.ingest(ContextItem(id="tr1", kind=ItemKind.tool_result, text=large_result, parent_id="tc1"))
pack = mgr.build_sync(phase=Phase.answer, query="user list")
print(f"Raw tool result size: {len(large_result)} chars")
print("\n=== Compiled Context ===")
print(pack.prompt)
print("\n=== Firewall Impact ===")
print(f"Prompt size after firewall: {len(pack.prompt)} chars")
print(f"Artifacts stored: {len(mgr.artifact_store.list_refs())}")
Run it:
python firewall_demo.py
Expected output excerpt:
Raw tool result size: 6087 chars
=== Compiled Context ===
[USER]
List all users
[TOOL RESULT [artifact:tr1]]
{"users": [{"id": 1, "name": "User1", "email": "user1@example.com"}, ...
[TOOL CALL]
list_users()
=== Firewall Impact ===
Prompt size after firewall: ... chars
Artifacts stored: 1
What just happened:
- The tool result was processed by the firewall during context build (all
tool_resultitems go through it by default). contextweaverstored the raw result in the artifact store.- The prompt kept only a compact summary plus an artifact reference instead of the full payload.
5. Try Tool Routing (2 minutes)
Problem: when a catalog grows, the model should only see the most relevant tools.
Save this as routing_demo.py:
"""Route a natural-language request to a focused tool shortlist."""
from contextweaver.routing.catalog import Catalog
from contextweaver.routing.router import Router
from contextweaver.routing.tree import TreeBuilder
from contextweaver.types import SelectableItem
catalog = Catalog()
catalog.register(SelectableItem(id="t1", kind="tool", name="send_email", description="Send email to a recipient", tags=["notify", "team", "message"]))
catalog.register(SelectableItem(id="t2", kind="tool", name="db_query", description="Query the database", tags=["data"]))
catalog.register(SelectableItem(id="t3", kind="tool", name="create_ticket", description="Create support ticket", tags=["support"]))
catalog.register(SelectableItem(id="t4", kind="tool", name="send_sms", description="Send SMS message", tags=["notify", "team", "message"]))
catalog.register(SelectableItem(id="t5", kind="tool", name="schedule_meeting", description="Schedule a calendar meeting", tags=["calendar"]))
graph = TreeBuilder(max_children=3).build(catalog.all())
router = Router(graph, items=catalog.all(), beam_width=2, top_k=2)
result = router.route("notify the team about the deadline")
print("=== Query ===")
print("notify the team about the deadline")
print("\n=== Top Tools ===")
for item_id in result.candidate_ids:
item = catalog.get(item_id)
print(f"- {item.name}: {item.description}")
Run it:
python routing_demo.py
Expected output:
=== Query ===
notify the team about the deadline
=== Top Tools ===
- send_sms: Send SMS message
- send_email: Send email to a recipient
What just happened:
TreeBuilderorganized the catalog into a bounded routing graph.Routerscored the query against that graph and returned the top two candidates.- Your model would now see a focused shortlist instead of the full catalog.
6. What to Try Next (1 minute)
Available now:
- README for the top-level package overview
- Concepts for phases, the context firewall, and routing terms
- Architecture for the pipeline stages and module layout
- MCP Integration for MCP adapters and session ingestion
- A2A Integration for multi-agent adapter flows
- Examples directory for larger end-to-end demos
Planned separately:
- Framework-specific integration guides are tracked in separate issues and are not part of this quickstart.
If you want a deeper local smoke test after this guide, run:
python -m contextweaver demo