Skip to content

MCP Integration

contextweaver provides an adapter for the Model Context Protocol (MCP) that converts MCP tool definitions and results into contextweaver's native types.

Adapter functions

mcp_tool_to_selectable(tool_dict)

Converts an MCP tool definition dict into a SelectableItem:

from contextweaver.adapters.mcp import mcp_tool_to_selectable

mcp_tool = {
    "name": "search_database",
    "description": "Search records in the database",
    "inputSchema": {
        "type": "object",
        "properties": {
            "query": {"type": "string"},
            "limit": {"type": "integer", "default": 10}
        }
    },
    "outputSchema": {
        "type": "object",
        "properties": {
            "results": {"type": "array"},
            "total": {"type": "integer"}
        }
    }
}

item = mcp_tool_to_selectable(mcp_tool)
# item.id            == "mcp:search_database"
# item.kind          == "tool"
# item.name          == "search_database"
# item.output_schema == {"type": "object", ...}

If the tool definition includes an outputSchema, it is preserved in item.output_schema. When absent the field is None.

The namespace is inferred automatically from the tool name prefix:

Tool name Inferred namespace
github.create_issue github
filesystem/read filesystem
slack_send_message slack
search_database mcp (fallback)

Use infer_namespace(tool_name) directly if you need the logic outside of mcp_tool_to_selectable().

mcp_result_to_envelope(result_dict, tool_name)

Converts an MCP tool result dict into a ResultEnvelope:

from contextweaver.adapters.mcp import mcp_result_to_envelope

mcp_result = {
    "content": [{"type": "text", "text": "Found 42 records matching query"}],
    "isError": False
}

envelope, binaries, full_text = mcp_result_to_envelope(mcp_result, "search_database")
# envelope.summary contains truncated text (max 500 chars)
# full_text contains the complete untruncated text
# envelope.status  == "ok"
# binaries maps handle → (raw_bytes, media_type, label)

Supported content types

Content type Handling
text Concatenated into full_text and summary
image Base64-decoded; stored as binary artifact
audio Base64-decoded; stored as binary artifact (e.g. audio/wav)
resource Text extracted into full_text; raw bytes stored as artifact
resource_link URI stored as ArtifactRef; URI string in binaries for caller resolution

Structured content

If the result contains a top-level structuredContent dict, it is serialized as a JSON artifact and its top-level keys are extracted as facts:

mcp_result = {
    "content": [{"type": "text", "text": "query done"}],
    "structuredContent": {"count": 42, "status": "done"},
}
envelope, binaries, _ = mcp_result_to_envelope(mcp_result, "query")
# binaries["mcp:query:structured_content"] → JSON bytes
# envelope.facts includes "count: 42", "status: done"

Content-part annotations

Per-part annotations (with audience and priority fields) are collected into envelope.provenance["content_annotations"]:

mcp_result = {
    "content": [
        {"type": "text", "text": "...", "annotations": {"audience": ["human"], "priority": 0.9}},
    ],
}
envelope, _, _ = mcp_result_to_envelope(mcp_result, "tool")
# envelope.provenance["content_annotations"] == [{"part_index": 0, "audience": ["human"], ...}]

load_mcp_session_jsonl(path)

Loads a JSONL session file containing MCP-style events and returns a list of ContextItem objects:

from contextweaver.adapters.mcp import load_mcp_session_jsonl

items = load_mcp_session_jsonl("examples/data/mcp_session.jsonl")
for item in items:
    print(f"{item.kind.value}: {item.text[:60]}...")

Session JSONL format

Each line is a JSON object with at minimum id, type, and either text or content:

{"id": "u1", "type": "user_turn", "text": "Search for open invoices"}
{"id": "tc1", "type": "tool_call", "text": "invoices.search(status='open')", "parent_id": "u1"}
{"id": "tr1", "type": "tool_result", "content": "...", "parent_id": "tc1"}

See examples/data/mcp_session.jsonl for a complete example.

End-to-end example

from contextweaver.adapters.mcp import (
    load_mcp_session_jsonl,
    mcp_tool_to_selectable,
)
from contextweaver.context.manager import ContextManager
from contextweaver.types import ItemKind, Phase

# Load session events
items = load_mcp_session_jsonl("examples/data/mcp_session.jsonl")

# Build context with firewall
mgr = ContextManager()
for item in items:
    if item.kind == ItemKind.tool_result and len(item.text) > 2000:
        mgr.ingest_tool_result(
            tool_call_id=item.parent_id or item.id,
            raw_output=item.text,
            tool_name="mcp_tool",
        )
    else:
        mgr.ingest(item)

pack = mgr.build_sync(phase=Phase.answer, query="invoice status")
print(pack.prompt)

See examples/mcp_adapter_demo.py for the full runnable demo.