Skip to content
Monitoring
Skill

/maple-agent-tracing-microsoft-agent-framework

Trace Microsoft Agent Framework and Semantic Kernel agents (Python and .NET) with Maple: native GenAI spans exported over OTLP/HTTP, plus a span processor that adds the conversation id so each chat is one Agent Session with transcript, tools and tokens. Triggers on 'trace my

BOOST
From plugin
maple
1.8k37 skills
Install
$ npx -y skills add mapletechlabs/maple --skill maple-agent-tracing-microsoft-agent-framework --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/maple-agent-tracing-microsoft-agent-framework

Context preview

The summary Claude sees to decide when to auto-load this skill.

Trace Microsoft Agent Framework and Semantic Kernel agents (Python and .NET) with Maple: native GenAI spans exported over OTLP/HTTP, plus a span processor that adds the conversation id so each chat is one Agent Session with transcript, tools and tokens. Triggers on 'trace my

SKILL.md

maple-agent-tracing-microsoft-agent-framework.SKILL.md
name: maple-agent-tracing-microsoft-agent-framework
description: "Trace Microsoft Agent Framework and Semantic Kernel agents (Python and .NET) with Maple: native GenAI spans exported over OTLP/HTTP, plus a span processor that adds the conversation id so each chat is one Agent Session with transcript, tools and tokens. Triggers on 'trace my agent framework agent', 'add Maple to Microsoft Agent Framework', 'add Maple to Semantic Kernel', 'agent sessions for agent-framework', 'OpenTelemetry for Semantic Kernel'."

Maple agent tracing: Microsoft Agent Framework and Semantic Kernel

Goal: one user conversation = one Maple Agent Session, with a transcript, every model call, every tool call (arguments, results, failures) and tokens.

The framework emits the spans itself. You add: an OTLP/HTTP exporter, content capture, a `gen_ai.conversation.id` span processor (the framework never sets one for local-history sessions), and a flush.

Step 0: Detect

  • Python MAF: `agent-framework`, `agent-framework-core` in `pyproject.toml` / `requirements*.txt` / `uv.lock`. Check the installed version (`python -c "import agent_framework; print(agent_framework.__version__)"`). Target ≥ 1.19.0; upgrade if older (1.13 lacks the `otlp_*` arguments used below).
  • .NET MAF: `Microsoft.Agents.AI` in `*.csproj`. Target ≥ 1.22.0.
  • Semantic Kernel: `semantic-kernel` (Python, target ≥ 1.44.1) or `Microsoft.SemanticKernel` (.NET). Use the Semantic Kernel section.
  • Existing OpenTelemetry: search for `TracerProvider(`, `set_tracer_provider`, `configure_azure_monitor`, `logfire.configure`, `configure_otel_providers`, `AddOpenTelemetry(`, `Sdk.CreateTracerProviderBuilder`. If a provider exists, add Maple's exporter and the conversation processor to it; do not create a second provider and do not call `configure_otel_providers()`.
  • Find every place a user message is handled (HTTP route, queue consumer, CLI loop) and what identifies the conversation there (chat id, thread id, `AgentSession`). You need it in Step 3.

Step 1: Key and region

  • US endpoint `https://ingest.maple.dev`, EU `https://ingest.eu.maple.dev`. Header `Authorization=Bearer <key>`. Protocol `http/protobuf`.
  • Key in the user's prompt: use it. No key: use the literal `MAPLE_TEST` (ingest accepts and discards it) and tell the user to replace it with their key from Settings → Ingestion.
  • Never put a private `maple_sk_` key in browser code.
  • Follow the repo's existing secret/env convention (`.env`, settings class, user-secrets). If there is none, inlining the ingest key is acceptable: ingest keys are write-only.
  • Key from an env var: when it's unset, log one warning (`MAPLE_INGEST_KEY is not set; Maple telemetry export is disabled`) and skip the Maple exporter so the app runs normally (Step 2 shows it). Never raise, exit or throw over the key, no bare `KeyError` on `os.environ['MAPLE_INGEST_KEY']`, and never pass a null `Environment.GetEnvironmentVariable(...)` into the .NET header (`Bearer ` with no key is an opaque 401).
  • App loads `.env` (`load_dotenv()`): call it before `configure_otel_providers()` / the provider is built, and before reading the key. Otherwise the exporter silently targets `localhost:4318` with no key.

Step 2: Install and init

Python MAF

pip install "agent-framework-core>=1.19.0" "agent-framework-openai>=1.14.4" opentelemetry-exporter-otlp-proto-http

(Keep the `agent-framework` meta-package if the project already uses it; still add `opentelemetry-exporter-otlp-proto-http`, MAF installs no exporter.)

Create `maple_tracing.py` next to the app entry point:

from contextlib import contextmanager
from contextvars import ContextVar

from opentelemetry.sdk.trace import SpanProcessor

_conversation_id: ContextVar[str | None] = ContextVar("conversation_id", default=None)


class ConversationIdProcessor(SpanProcessor):
    """Puts gen_ai.conversation.id on every span started inside `conversation()`."""

    def on_start(self, span, parent_context=None):
        if (conversation_id := _conversation_id.get()) is not None:
            span.set_attribute("gen_ai.conversation.id", conversation_id)


@contextmanager
def conversation(conversation_id: str):
    token = _conversation_id.set(conversation_id)
    try:
        yield
    finally:
        _conversation_id.reset(token)

At startup, once, before agents are created:

import logging
import os

from agent_framework.observability import configure_otel_providers
from opentelemetry import trace

from maple_tracing import ConversationIdProcessor

key = os.environ.get("MAPLE_INGEST_KEY")
if key:
    configure_otel_providers(
        service_name="<service-name>",
        resource_attributes={"deployment.environment.name": "<env>"},
        otlp_endpoint="https://ingest.maple.dev",
        otlp_protocol="http/protobuf",
        otlp_headers={"Authorization": f"Bearer {key}"},
        enable_sensitive_data=True,
        enable_message_events=False,
    )
    trace.get_tracer_provider().add_span_processor(ConversationIdProcessor())
else:
    # A missing key disables export; it never stops the app.
    logging.getLogger(__name__).warning("MAPLE_INGEST_KEY is not set; Maple telemetry export is disabled")

Equivalent env-var config, with a bare `configure_otel_providers()` call:

export OTEL_SERVICE_NAME="<service-name>"
export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.maple.dev"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer <key>"
export ENABLE_SENSITIVE_DATA="true"
export ENABLE_MESSAGE_EVENTS="false"
  • `otlp_protocol` is mandatory: MAF defaults to gRPC (spec says http/protobuf) and fails silently or with an ImportError.
  • `configure_otel_providers()` appends `/v1/traces`, `/v1/metrics`, `/v1/logs` to the endpoint. `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`, if set, is used as-is and must include `/v1/traces`.
  • Existing provider instead: add `BatchSpanProcessor(OTLPSpanExporter(en
Read more
Ships withmaple

OpenTelemetry observability platform

Get the whole plugin

Other skills on maple.