<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: WonderLab</title>
    <description>The latest articles on DEV Community by WonderLab (@wonderlab).</description>
    <link>https://dev.to/wonderlab</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3797373%2F25beba30-d8d4-4d2e-9ec6-170356089350.jpg</url>
      <title>DEV Community: WonderLab</title>
      <link>https://dev.to/wonderlab</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wonderlab"/>
    <language>en</language>
    <item>
      <title>AI Evaluation Series (03): LLM-as-Judge — How to Use LLMs to Evaluate LLMs Correctly</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Tue, 21 Jul 2026 03:44:16 +0000</pubDate>
      <link>https://dev.to/wonderlab/ai-evaluation-series-03-llm-as-judge-how-to-use-llms-to-evaluate-llms-correctly-42mg</link>
      <guid>https://dev.to/wonderlab/ai-evaluation-series-03-llm-as-judge-how-to-use-llms-to-evaluate-llms-correctly-42mg</guid>
      <description>&lt;h2&gt;
  
  
  How LLM-as-Judge Works
&lt;/h2&gt;

&lt;p&gt;LLM-as-Judge delegates the quality evaluation of one LLM's output to another LLM (or the same one).&lt;/p&gt;

&lt;p&gt;Two basic forms:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pointwise scoring (single answer)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;JUDGE_PROMPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Evaluate this AI response (1-10 per dimension):
Dimensions: accuracy, relevance, clarity
Question: {question}
Response: {answer}
Return JSON: {{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accuracy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: int, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;relevance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: int, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;clarity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: int}}&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pairwise comparison (two answers)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;COMPARE_PROMPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Which answer is better?
Question: {question}
Answer A: {answer_a}
Answer B: {answer_b}
Reply with only &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; or &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;B&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pairwise works well for comparing two versions (Prompt A vs Prompt B). Pointwise works better for monitoring a single system's quality trend over time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three Experiments: How Severe Are the Biases?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Experiment 1: Position Bias
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Design:&lt;/strong&gt; Same answer pair, evaluated twice — first with A shown first, then with B shown first. Without bias, the winner should be independent of order; first-position win rate should be ~50%.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;experiment_position_bias&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;answer_pairs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;winner_order1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;judge_pairwise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;answer_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;answer_b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# A first
&lt;/span&gt;        &lt;span class="n"&gt;winner_order2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;judge_pairwise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;answer_b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;answer_a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# B first
&lt;/span&gt;        &lt;span class="c1"&gt;# Unbiased: winner_order1 == winner_order2 rate should approach 100%
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Results:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;First-position win rate:  67%  (unbiased baseline: 50%)
Second-position win rate: 33%
Consistency rate:         67%  (unbiased baseline: 100%)

Position bias magnitude:  17% above 50/50 baseline
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A consistency rate of 67% means 33% of judgments flip when the same answer pair swaps order. If you use this Judge for A/B testing, one third of the results track presentation order, not answer quality.&lt;/p&gt;

&lt;h3&gt;
  
  
  Experiment 2: Verbosity Bias
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Design:&lt;/strong&gt; Same question, two answer versions — a concise version (2-3 sentences) and a verbose version with identical core content but padded with filler, repetition, and elaboration. Content is equivalent; only length differs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Concise version:
Lists are mutable (add/remove/change); tuples are immutable.
Tuples are faster, use less memory, and work as dictionary keys or multi-return values.

Verbose version:
That's a great question! Let me explain in detail...
(same core information, expanded to ~300 words with intros, repetition, and summary)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Results:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Avg concise answer score:  7.5/10
Avg verbose answer score:  8.0/10
Verbosity bonus:           +0.5 points
(unbiased baseline: 0.0 point difference)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Equal content, and the longer version scores 0.5 points higher. Evaluating two prompts where one produces concise answers and the other verbose ones, the Judge systematically favors verbosity — independent of which version actually helps the user more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Experiment 3: Framing Bias
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Design:&lt;/strong&gt; Same answer, evaluated with two prompts — a standard Judge prompt vs a strict expert prompt (explicitly stating that ordinary answers don't exceed 6/10, only excellent ones reach 8+).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Results:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Standard prompt avg score:      8.0/10
Strict expert prompt avg score: 8.0/10
Framing delta:                  +0.00 points
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this means:&lt;/strong&gt; glm-4-flash doesn't respond to the strictness level specified in the prompt — even with an explicit higher standard, scores stay the same. This is useful information: for this model, you don't need to worry that prompt wording will systematically inflate or deflate scores. But stronger models (GPT-4, Claude) may show more framing sensitivity. Always test before assuming.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three Mitigation Strategies
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mitigate Position Bias: Multi-Round Randomization
&lt;/h3&gt;

&lt;p&gt;Single evaluations are unreliable with 33% flip rate. Fix: evaluate each pair multiple times with randomized order.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pairwise_with_randomization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;judge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;answer_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;answer_b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rounds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;a_wins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;b_wins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rounds&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;judge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;answer_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;answer_b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;a_wins&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;b_wins&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;judge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;answer_b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;answer_a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;b_wins&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="c1"&gt;# "A" position holds B
&lt;/span&gt;            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;a_wins&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a_win_rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;a_wins&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;rounds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;b_win_rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;b_wins&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;rounds&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5 rounds of randomization averages position bias toward 50%. Cost is 5x API calls; reliability is substantially higher.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mitigate Verbosity Bias: Per-Dimension Scoring + Anti-Verbose Instructions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Option A: Score dimensions separately, no combined score&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;ANTI_VERBOSE_JUDGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Evaluate this response. Score each dimension 1-5 independently:

1. Accuracy: Is the technical content correct? (length-independent)
2. Conciseness: Is the information density high? (more concise = higher score)
3. Relevance: Does it directly answer the question?

Note: longer answers are not better — evaluate content quality only.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option B: Explicit declaration in the prompt&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Note: response length does not affect scoring. A concise accurate answer and a detailed accurate answer should receive equal scores.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Mitigate Self-Bias: Use a Stronger or Different Judge Model
&lt;/h3&gt;

&lt;p&gt;If you generate with one model and evaluate with the same model, self-bias leads to systematic overestimation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Generate with cheaper model
&lt;/span&gt;&lt;span class="n"&gt;generator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;glm-4-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt;

&lt;span class="c1"&gt;# Evaluate with stronger or different-vendor model
&lt;/span&gt;&lt;span class="n"&gt;judge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt;
&lt;span class="c1"&gt;# or
&lt;/span&gt;&lt;span class="n"&gt;judge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatAnthropic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-sonnet-4-6&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cost increases; evaluation reliability improves. For important release decisions, use Claude to evaluate GPT output (or vice versa) rather than self-evaluation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Production-Ready Judge Prompt Template
&lt;/h2&gt;

&lt;p&gt;Incorporating all three mitigations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;PRODUCTION_JUDGE_PROMPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;You are a strict technical content reviewer.

Scoring rules:
- Score range 1-5, NOT 1-10 (reduces variance)
- Length does not affect scores — evaluate content quality only
- Vague or verbose writing is NOT a positive — clarity and conciseness are
- 3 means &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;meets expectations&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, not &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;barely acceptable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;

Dimensions (each 1-5):
1. Accuracy: Is the technical content correct? Any factual errors?
2. Relevance: Does it directly answer the question? Any off-topic content?
3. Usefulness: Can a user act on this answer to solve their problem?

Question: {question}
Response: {answer}

Return JSON only:
{{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accuracy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: int, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;relevance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: int, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usefulness&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: int, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reasoning&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;one sentence explaining the lowest-scoring dimension&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;}}&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Design choices:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1-5 not 1-10&lt;/strong&gt;: reduces variance; LLMs are inconsistent at fine-grained distinctions (7 vs 8)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"3 = meets expectations"&lt;/strong&gt;: prevents LLMs from treating 3 as negative, which causes score inflation across the board&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Require reasoning&lt;/strong&gt;: forces the Judge to explain the lowest-scoring dimension, reducing arbitrary scoring&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anti-verbosity declaration&lt;/strong&gt;: explicitly counters verbosity bias&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Practical Recommendations
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Single evaluations have limited reliability:&lt;/strong&gt; With 33% flip probability from position bias, single results aren't enough to make decisions. For important comparisons, run at least 3 rounds and report win rates, not single outcomes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Calibrate before scaling:&lt;/strong&gt; Before running a new Judge Prompt at scale, test it against 20-30 human-annotated samples. Spearman correlation between Judge scores and human ratings should exceed 0.7 before using the Judge for decision-making.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitor score distributions:&lt;/strong&gt; If the Judge assigns 90% of outputs a score of 7-8, the distribution is too concentrated and the Judge lacks discriminative power. Adjust the prompt or use a stronger model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use scenario-specific Judges:&lt;/strong&gt; A code generation Judge should focus on correctness and runnability; a summarization Judge should focus on faithfulness; a conversational AI Judge should focus on helpfulness. A generic Judge applied to all scenarios loses evaluation precision.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Three experiments produce three specific action items:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Position bias (67% first-position win rate)&lt;/strong&gt;: Pairwise evaluation must randomize order across multiple rounds — 33% flip probability makes single-shot results unreliable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verbosity bias (+0.5 points)&lt;/strong&gt;: Declare "length doesn't affect scoring" in the prompt, and add a conciseness dimension to counteract&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framing bias (0.0 delta on glm-4-flash)&lt;/strong&gt;: This model doesn't respond to prompt strictness level — but different models behave differently, always verify before assuming&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://arxiv.org/abs/2306.05685" rel="noopener noreferrer"&gt;Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena (Zheng et al., 2023)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Full demo code: &lt;a href="https://github.com/chendongqi/llm-in-action/tree/main/eval-03-llm-judge" rel="noopener noreferrer"&gt;eval-03-llm-judge&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Check out &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — a curated marketplace of AI agents and skills that have been validated in real-world, enterprise-grade workflows. No fluff, just what actually works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Find more useful knowledge and interesting products on my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;Homepage&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>evaluation</category>
      <category>llm</category>
      <category>engineering</category>
    </item>
    <item>
      <title>Open Source Project of the Day (#128): BISHENG — Open-Source Enterprise LLM DevOps Platform</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Tue, 21 Jul 2026 03:42:59 +0000</pubDate>
      <link>https://dev.to/wonderlab/open-source-project-of-the-day-128-bisheng-open-source-enterprise-llm-devops-platform-30ec</link>
      <guid>https://dev.to/wonderlab/open-source-project-of-the-day-128-bisheng-open-source-enterprise-llm-devops-platform-30ec</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"Bi Sheng invented movable type printing. BISHENG helps enterprises 'typeset' large language model capabilities into their own business processes with the same kind of flexible, recomposable modularity."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is article &lt;strong&gt;#128&lt;/strong&gt; in the &lt;em&gt;Open Source Project of the Day&lt;/em&gt; series. Today's project is &lt;strong&gt;BISHENG&lt;/strong&gt; — DataElem's open-source enterprise LLM application DevOps platform.&lt;/p&gt;

&lt;p&gt;Enterprises adopting LLMs frequently encounter a painful fragmentation: one tool specializes in RAG, another in workflow orchestration, another in model management, and yet another in permissions. Each solves one piece. Stitching them together creates a maintenance burden that compounds over time.&lt;/p&gt;

&lt;p&gt;BISHENG positions as a one-stop solution: document parsing, knowledge base construction, RAG retrieval, visual workflow orchestration, multi-agent collaboration, model fine-tuning management, and enterprise permissions governance — all in a single platform.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You'll Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;AGL (Agent Guidance Language) framework: how to encode domain expert experience into AI agents&lt;/li&gt;
&lt;li&gt;Visual workflow builder's Human-in-the-Loop design&lt;/li&gt;
&lt;li&gt;High-precision document parsing: OCR trained on 5+ years of proprietary data&lt;/li&gt;
&lt;li&gt;Enterprise RAG use cases: from policy comparison to resume screening&lt;/li&gt;
&lt;li&gt;BISHENG's technical architecture: Milvus + Elasticsearch + OnlyOffice + LLaMA-Factory&lt;/li&gt;
&lt;li&gt;v2.6.0's Linsight Task Mode&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Familiarity with enterprise LLM application concepts (RAG, Agent, workflow)&lt;/li&gt;
&lt;li&gt;Experience with or interest in enterprise AI system development&lt;/li&gt;
&lt;li&gt;Docker basics (for local deployment)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Project Background
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why "Bi Sheng"
&lt;/h3&gt;

&lt;p&gt;The project is named after Bi Sheng, the 11th-century inventor of movable type printing. The metaphor is fitting: where woodblock printing was rigid and inflexible, movable type was modular and recomposable. BISHENG aims to bring the same flexibility to enterprise AI capability building — not hardcoded fixed pipelines, but modular, orchestratable, iterable components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Author / Team
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Company&lt;/strong&gt;: DataElem (Shanghai)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License&lt;/strong&gt;: Apache-2.0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version&lt;/strong&gt;: v2.6.0 (July 2026)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User base&lt;/strong&gt;: Fortune 500 companies and industry-leading organizations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project Stats
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⭐ GitHub Stars: &lt;strong&gt;11,600+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🍴 Forks: 1,900+&lt;/li&gt;
&lt;li&gt;📦 Releases: 73&lt;/li&gt;
&lt;li&gt;📄 License: Apache-2.0&lt;/li&gt;
&lt;li&gt;💻 Language: Python ~50% + TypeScript ~47.5%&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Core Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Linsight Agent (AGL Framework)
&lt;/h3&gt;

&lt;p&gt;AGL (Agent Guidance Language) is BISHENG's proprietary agent guidance framework, addressing a core enterprise AI problem: &lt;strong&gt;how do you encode a specific domain expert's experience, preferences, and business logic into an AI, so it operates at expert level in professional scenarios?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A generic LLM performing financial analysis might produce technically correct but business-noncompliant conclusions. AGL lets you explicitly embed compliance specialists' judgment logic, common business edge cases, and industry-specific analytical perspectives into the agent's decision process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Linsight Task Mode (v2.6.0)&lt;/strong&gt;: Agents don't just answer questions — they can proactively plan and execute multi-step tasks, delivering formatted reports that meet enterprise requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Visual Workflow Builder
&lt;/h3&gt;

&lt;p&gt;This is BISHENG's most differentiating core capability:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Single framework for all task types.&lt;/strong&gt; Many platforms separate "chatflow" and "workflow" into independent modules with incompatible interfaces. BISHENG uses one visual workflow engine for every scenario.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Supported flow control:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Loop&lt;/strong&gt;: Agent repeats until a condition is met&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallelism&lt;/strong&gt;: Multiple branches execute simultaneously&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch processing&lt;/strong&gt;: Execute the same flow for each item in a list&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conditional branching&lt;/strong&gt;: Route to different paths based on intermediate results&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Human-in-the-Loop:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An essential capability for enterprise scenarios:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Workflow running...
    ↓
Reaches "Contract Risk Assessment" node
    ↓
Legal Agent outputs preliminary assessment
    ↓
[PAUSE — waiting for human review]
    ↓  Human reviews, approves / modifies / rejects
    ↓
Continue with subsequent steps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This design inserts human judgment at high-risk decision points, rather than running fully automated from end to end. For enterprises with strict compliance requirements (finance, legal, healthcare), this is a hard requirement, not a nice-to-have.&lt;/p&gt;

&lt;h3&gt;
  
  
  High-Precision Document Parsing
&lt;/h3&gt;

&lt;p&gt;BISHENG has a distinctive advantage in document parsing — not an integrated third-party tool, but a proprietary model DataElem trained on &lt;strong&gt;5+ years of proprietary data&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coverage:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Printed text recognition (common and rare characters)&lt;/li&gt;
&lt;li&gt;Handwritten text recognition (including Chinese handwriting)&lt;/li&gt;
&lt;li&gt;Complex table recognition (including merged cells across rows and columns)&lt;/li&gt;
&lt;li&gt;Document layout analysis (multi-column, embedded images, header/footer separation)&lt;/li&gt;
&lt;li&gt;Seal/stamp recognition&lt;/li&gt;
&lt;li&gt;Chart and figure recognition&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Deployment&lt;/strong&gt;: Fully private deployment, no external API calls required, data never leaves the intranet.&lt;/p&gt;

&lt;p&gt;This capability is particularly valuable in Chinese enterprise document processing: vast amounts of scanned paper forms, handwritten approval slips, and stamped documents are persistent pain points for traditional OCR tools. BISHENG's models have domain-specific optimization for exactly this type of data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enterprise RAG Knowledge Base
&lt;/h3&gt;

&lt;p&gt;Complete enterprise knowledge base pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Source documents (PDF/Word/Excel/scans)
        ↓
Document parsing (high-precision OCR + layout analysis)
        ↓
Text chunking + vectorization (Milvus)
        ↓
Hybrid retrieval (vector similarity + full-text search via Elasticsearch)
        ↓
Knowledge base Q&amp;amp;A / citation tracing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Typical enterprise scenarios:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Policy comparison&lt;/td&gt;
&lt;td&gt;Clause-by-clause diff of new vs. old policy versions, generating change reports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contract review&lt;/td&gt;
&lt;td&gt;Automated comparison of contract terms against company standards, flagging risk points&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Meeting minutes&lt;/td&gt;
&lt;td&gt;Audio/video → structured minutes, automatic action item tracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resume screening&lt;/td&gt;
&lt;td&gt;Multi-dimensional matching of candidate resumes against job requirements, scoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Call record analysis&lt;/td&gt;
&lt;td&gt;Structured analysis of customer service calls, key information extraction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unstructured data governance&lt;/td&gt;
&lt;td&gt;Historical document classification, tagging, knowledge graph construction&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Unified Model Management
&lt;/h3&gt;

&lt;p&gt;Full lifecycle management from model selection to production operation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model integration&lt;/strong&gt;: OpenAI-compatible API / local deployment (llama.cpp, vLLM, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dataset management&lt;/strong&gt;: Version control and quality control for labeled data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SFT fine-tuning&lt;/strong&gt;: LLaMA-Factory integration for fine-tuning on private data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evaluation&lt;/strong&gt;: Build evaluation sets, quantify model performance on business scenarios&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment management&lt;/strong&gt;: Different departments using different model versions, traffic control, A/B testing&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Enterprise-Grade Features
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Permissions and security:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RBAC (role-based access control)&lt;/li&gt;
&lt;li&gt;User group management (different permissions per department)&lt;/li&gt;
&lt;li&gt;SSO single sign-on (LDAP support)&lt;/li&gt;
&lt;li&gt;Per-group traffic control quotas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Operations and compliance:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High-availability deployment&lt;/li&gt;
&lt;li&gt;Vulnerability scanning and security patching&lt;/li&gt;
&lt;li&gt;Usage statistics and audit logs&lt;/li&gt;
&lt;li&gt;Observability monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Document editing:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OnlyOffice integration for online collaborative editing of Word/Excel/PPT&lt;/li&gt;
&lt;li&gt;Fits the "AI generates draft → human edits online → final output" workflow pattern&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Quick Deployment
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;System requirements:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU: ≥ 4 cores (18 vCPUs recommended)&lt;/li&gt;
&lt;li&gt;RAM: ≥ 16 GB (48 GB recommended)&lt;/li&gt;
&lt;li&gt;Docker 19.03.9+, Docker Compose 1.25.1+
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/dataelement/bisheng.git
&lt;span class="nb"&gt;cd &lt;/span&gt;bisheng/docker
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.yml &lt;span class="nt"&gt;-p&lt;/span&gt; bisheng up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Access via &lt;code&gt;http://IP:3001&lt;/code&gt;. First registered user automatically becomes system admin.&lt;/p&gt;




&lt;h2&gt;
  
  
  Technical Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frontend (React + TypeScript)
    ↓
Backend (Python + FastAPI)
    ├── Workflow engine (LangChain/Langflow-based, customized)
    ├── Document parsing (proprietary OCR models)
    ├── Agent framework (AGL framework)
    └── Model management (LLaMA-Factory integration)
    ↓
Data layer
    ├── Milvus (vector storage)
    ├── Elasticsearch (full-text search)
    ├── MySQL (metadata)
    └── MinIO/S3 (file storage)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Acknowledgements&lt;/strong&gt;: Built on LangChain, Langflow, Unstructured, and LLaMA-Factory, with extensive enterprise customization on top.&lt;/p&gt;




&lt;h2&gt;
  
  
  Links and Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🌟 &lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/dataelement/bisheng" rel="noopener noreferrer"&gt;dataelement/bisheng&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🌐 &lt;strong&gt;Website&lt;/strong&gt;: &lt;a href="https://bisheng.dataelem.com" rel="noopener noreferrer"&gt;bisheng.dataelem.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📖 &lt;strong&gt;Documentation&lt;/strong&gt;: &lt;a href="https://github.com/dataelement/bisheng/wiki" rel="noopener noreferrer"&gt;github.com/dataelement/bisheng/wiki&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;BISHENG's core value is &lt;strong&gt;reducing the integration complexity of enterprise AI development&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Building an LLM application for an enterprise typically requires separately selecting a document parsing tool, RAG framework, workflow orchestration platform, agent framework, model management system, and permissions infrastructure — then connecting them all together. That integration process alone consumes substantial engineering resources. BISHENG consolidates all of these into a single platform with a unified data model, unified permissions, and unified monitoring.&lt;/p&gt;

&lt;p&gt;High-precision document parsing is an underappreciated differentiator. In Chinese enterprise contexts, vast amounts of critical information still exists in scanned documents, handwritten forms, and stamped records. The quality of processing that data directly determines knowledge base usability. DataElem's 5+ years of proprietary training data represents a real advantage on this dimension.&lt;/p&gt;

&lt;p&gt;The AGL framework and Human-in-the-Loop workflow reflect serious consideration of what makes enterprise scenarios different from general-purpose AI: not all decisions can be fully automated, and the right design preserves space for human intervention at critical junctures.&lt;/p&gt;

&lt;p&gt;11.6k Stars and production use by Fortune 500 companies indicate this is not just a technical proof of concept — it's a product delivering measurable value.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Explore &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — A marketplace for handpicked AI Agents and skills. Each is validated in real enterprise workflows, stripping away hype and keeping only what truly works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Welcome to my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;Homepage&lt;/a&gt; for more useful insights and interesting products.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>llm</category>
      <category>rag</category>
      <category>workflow</category>
    </item>
    <item>
      <title>AI Evaluation Series (02): Metric Design — From Business Goals to Measurable Indicators</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Sun, 19 Jul 2026 01:11:20 +0000</pubDate>
      <link>https://dev.to/wonderlab/ai-evaluation-series-02-metric-design-from-business-goals-to-measurable-indicators-3269</link>
      <guid>https://dev.to/wonderlab/ai-evaluation-series-02-metric-design-from-business-goals-to-measurable-indicators-3269</guid>
      <description>&lt;h2&gt;
  
  
  What Happens Without Metrics
&lt;/h2&gt;

&lt;p&gt;A RAG Q&amp;amp;A system launches. The engineers say "all tests passed" — API response time under 2 seconds, correct format, no crashes.&lt;/p&gt;

&lt;p&gt;Two weeks later, users report "the AI often doesn't answer the actual question." Investigation reveals: retrieval recall is only 40%. More than half the user questions can't find relevant documents.&lt;/p&gt;

&lt;p&gt;The problem was there before launch. No one had designed a "retrieval quality" metric, so no one saw it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The L1/L2/L3 Framework
&lt;/h2&gt;

&lt;p&gt;Metrics organize into three layers, each addressing a different class of question:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;L1 — Business Outcome
  The end goal: is the AI system creating value?
  Examples: task completion rate, user adoption rate, user satisfaction

L2 — Output Quality
  The middle layer: how good is the AI's actual output?
  Examples: accuracy, relevance, completeness

L3 — System Health
  The foundation: is the system running stably?
  Examples: response latency, token cost, failure rate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Layer dependencies:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;L3 fails → L2 degrades (timeouts cause truncated output) → L1 drops (tasks fail)
L3 healthy, L2 poor → L1 still low (users don't adopt low-quality output)
All three healthy → L1 reflects real value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start diagnosing from L3 upward; reverse from L1 downward is much slower.&lt;/p&gt;




&lt;h2&gt;
  
  
  Metric Selection by Scenario
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Scenario 1: Document Q&amp;amp;A (RAG)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;L3 System health:
  Response latency P90     → &amp;lt; 5s
  Token cost per query     → &amp;lt; 3000
  Retrieval failure rate   → &amp;lt; 1%

L2 Output quality:
  Faithfulness             → no hallucinations beyond retrieved content (RAGAS)
  Answer Relevancy         → actually addresses the question (RAGAS)
  Context Precision        → fraction of retrieved content that's useful (RAGAS)
  Context Recall           → fraction of relevant content that was retrieved (RAGAS)

L1 Business outcome:
  Task completion rate     → fraction of users who mark "question resolved"
  Adoption rate            → fraction of users who copy/cite the AI response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The critical metric:&lt;/strong&gt; Context Recall — the most commonly skipped and most important RAG metric. If retrieval doesn't find the relevant content, generation quality is irrelevant.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 2: Code Generation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;L3:
  Response latency P90   → &amp;lt; 10s (code generation is slower; allow more)
  Excessive output rate  → avoid generating far more code than needed

L2:
  Syntax correctness     → can the output be parsed? (automatable)
  Test pass rate         → does the output pass unit tests? (automatable)
  Usability              → how much editing is needed? (human evaluation)
  Security scan pass     → does the output contain known vulnerabilities? (automatable)

L1:
  Adoption rate          → fraction of suggestions the user accepts
  Edit distance          → how much the user changed the suggestion (less = better)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The critical metric:&lt;/strong&gt; Test pass rate — fully automatable, measurable per commit, a natural fit for CI integration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 3: Document Summarization
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;L2:
  Faithfulness         → summary adds nothing not in the source (most important)
  Coverage             → source's key points appear in the summary
  Conciseness          → summary is noticeably shorter than source (otherwise no value)
  Readability          → LLM-as-Judge evaluation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The critical metric:&lt;/strong&gt; Faithfulness. Summarization's greatest risk is "adding" information that wasn't in the source (hallucination) — more harmful than omitting a detail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 4: Agent Task Completion
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;L2:
  Tool call accuracy   → correct tool selected, correct arguments (automatable)
  Step efficiency      → how many steps to complete the task (fewer is better)
  Trajectory quality   → is the reasoning path sound? (LLM-as-Judge)

L1:
  Task completion rate → did the task get done? (the core metric)
  First-run resolution → fraction completed without follow-up questions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The critical metric:&lt;/strong&gt; Task completion rate, but define "completed" carefully — "artifact exists" versus "artifact meets quality standard" are different requirements.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three Traps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Trap 1: Measuring Only L3
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✗ Wrong:
  "API returned 200, latency 1.2s, this version can go to production."

Problem: L3 health doesn't mean business value. The RAG example above — L3
         completely normal, Context Recall at 40%.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every release needs at least one L2 sampling evaluation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trap 2: Using BLEU/ROUGE for Semantic Quality
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Looks reasonable
&lt;/span&gt;&lt;span class="n"&gt;rouge_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rouge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;predictions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;references&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# The problem:
&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The capital of France is Paris.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;output_1&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Paris is the capital city of France.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# same meaning, low ROUGE
&lt;/span&gt;&lt;span class="n"&gt;output_2&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The capital of France is Paris, the capital.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# repetition, high ROUGE
&lt;/span&gt;
&lt;span class="c1"&gt;# ROUGE scores output_2 higher, but output_2 is clearly worse
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;BLEU/ROUGE measure word overlap, not semantic correctness. For generative outputs, use LLM-as-Judge instead. BLEU/ROUGE only work for tasks with standard reference answers, like machine translation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trap 3: Setting Thresholds by Instinct
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✗ Wrong:
  "Let's set Faithfulness &amp;gt; 0.8 as the quality gate."
  (Why 0.8? It sounds reasonable.)

✓ Right:
  Step 1: Run 100 samples, get the current baseline (e.g., mean = 0.73)
  Step 2: Manually inspect samples with Faithfulness &amp;lt; 0.6 — are they acceptable?
  Step 3: Set threshold based on that inspection (e.g., 0.65)
  Step 4: Document why samples below that threshold are unacceptable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thresholds come from data distributions and business acceptability, not from alignment with round numbers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Complete Metric Spec: Enterprise Document Q&amp;amp;A System
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# eval_metrics.yaml&lt;/span&gt;
&lt;span class="na"&gt;system&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;document-qa&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0"&lt;/span&gt;

&lt;span class="na"&gt;metrics&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;l3_system_health&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;response_latency_p90&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;5000ms"&lt;/span&gt;
      &lt;span class="na"&gt;collection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;trace_log&lt;/span&gt;
      &lt;span class="na"&gt;alert_threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;8000ms&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;token_cost_per_query&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;2000&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tokens"&lt;/span&gt;
      &lt;span class="na"&gt;collection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;llm_callback&lt;/span&gt;
      &lt;span class="na"&gt;alert_threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4000&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;retrieval_error_rate&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1%"&lt;/span&gt;
      &lt;span class="na"&gt;collection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;error_log&lt;/span&gt;

  &lt;span class="na"&gt;l2_output_quality&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;faithfulness&lt;/span&gt;
      &lt;span class="na"&gt;tool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ragas&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0.80"&lt;/span&gt;
      &lt;span class="na"&gt;collection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;weekly_sampling (n=100)&lt;/span&gt;
      &lt;span class="na"&gt;alert_threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.70&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;answer_relevancy&lt;/span&gt;
      &lt;span class="na"&gt;tool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ragas&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0.75"&lt;/span&gt;
      &lt;span class="na"&gt;collection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;weekly_sampling&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;context_recall&lt;/span&gt;
      &lt;span class="na"&gt;tool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ragas&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0.70"&lt;/span&gt;
      &lt;span class="na"&gt;collection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;weekly_sampling&lt;/span&gt;
      &lt;span class="na"&gt;note&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Most&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;important&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;retrieval&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;quality&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;metric"&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;format_compliance&lt;/span&gt;
      &lt;span class="na"&gt;tool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rule_check&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;100%"&lt;/span&gt;
      &lt;span class="na"&gt;collection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;every_request&lt;/span&gt;

  &lt;span class="na"&gt;l1_business_outcome&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;task_completion_rate&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;70%"&lt;/span&gt;
      &lt;span class="na"&gt;collection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;user_feedback_widget&lt;/span&gt;
      &lt;span class="na"&gt;note&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Fraction&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;clicking&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'this&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;solved&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;my&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;question'"&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;adoption_rate&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;50%"&lt;/span&gt;
      &lt;span class="na"&gt;collection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;behavior_tracking&lt;/span&gt;
      &lt;span class="na"&gt;note&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Fraction&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;who&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;copy/cite&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;AI&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;response"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Roadmap to a Working Metric System
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Step 1 (no tools needed, do now):
  □ Write down the system's core business goal (what is L1?)
  □ List 3-5 most important L2 output quality dimensions
  □ Confirm L3 basics are monitored (latency, error rate)

Step 2 (within 1 week):
  □ Manually evaluate 50 real use cases to get baseline numbers per L2 metric
  □ Set thresholds based on that baseline — not on intuition

Step 3 (ongoing):
  □ Run L2 sampling eval before each release; compare to baseline
  □ Check L1 data monthly; verify it moves consistently with L2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;L1/L2/L3 each have a distinct role&lt;/strong&gt;: L3 monitors system stability, L2 evaluates output quality, L1 validates business value — missing any one layer creates a blind spot&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scenario determines critical metrics&lt;/strong&gt;: RAG systems need Context Recall above all; code generation needs test pass rate; Agents need task completion rate — copying another system's metrics doesn't mean they fit yours&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thresholds come from data&lt;/strong&gt;: run a baseline first, then set the threshold — don't set a threshold and then justify it with data afterward&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;Check out &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — a curated marketplace of AI agents and skills that have been validated in real-world, enterprise-grade workflows. No fluff, just what actually works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Find more useful knowledge and interesting products on my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;Homepage&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>evaluation</category>
      <category>llm</category>
      <category>agents</category>
    </item>
    <item>
      <title>Open Source Project #127: wigolo — Zero API Key, $0/Query Local-First Web Intelligence for AI Agents, Compared Against Tavily / Exa / Firecrawl</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Sun, 19 Jul 2026 01:09:31 +0000</pubDate>
      <link>https://dev.to/wonderlab/open-source-project-127-wigolo-zero-api-key-0query-local-first-web-intelligence-for-ai-3f9h</link>
      <guid>https://dev.to/wonderlab/open-source-project-127-wigolo-zero-api-key-0query-local-first-web-intelligence-for-ai-3f9h</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"The go-to web for your AI coding agent — local-first search, fetch, crawl &amp;amp; research over MCP. No API keys, no cloud, $0/query."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is &lt;strong&gt;article #127&lt;/strong&gt; in the "One Open Source Project a Day" series. Today's project is &lt;strong&gt;wigolo&lt;/strong&gt; — a local-first web intelligence tool built specifically for AI agents, delivering search, crawl, fetch, and research capabilities over MCP with no API keys and zero per-query cost.&lt;/p&gt;

&lt;p&gt;Equipping an AI agent with "web search" is a problem that looks simple on the surface but has surprisingly deep layers. Tavily, Exa, and Firecrawl have each carved out mature niches in the cloud space — but they share a fundamental trait: per-query billing, data leaving your machine, and mandatory account registration. wigolo's premise: search engines' raw data is public, ranking and extraction algorithms can run locally, and the agent's query history can be cached into a local semantic index. Why should every agent search cost money and travel to the cloud?&lt;/p&gt;

&lt;p&gt;1,218 Stars. Created April 2026. Public beta.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You'll Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;wigolo's 10 tools and the design logic behind each&lt;/li&gt;
&lt;li&gt;What "byte-pinned source provenance" and "explainable scores" mean, and why they matter&lt;/li&gt;
&lt;li&gt;Local-first architecture: what runs on-device, what optionally reaches the cloud&lt;/li&gt;
&lt;li&gt;Head-to-head comparison with Tavily, Exa, Firecrawl — each tool's differentiated positioning&lt;/li&gt;
&lt;li&gt;What scenarios favor wigolo, and where cloud tools are still the better choice&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Experience with Claude Code or any AI agent&lt;/li&gt;
&lt;li&gt;Basic familiarity with MCP (Model Context Protocol)&lt;/li&gt;
&lt;li&gt;General understanding of AI agent tool-calling mechanics&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Project Background
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Overview
&lt;/h3&gt;

&lt;p&gt;wigolo is a Node.js process that exposes 10 web-related tools as an MCP server, letting AI agents call search, fetch, crawl, extract, and other functions over the standard MCP protocol.&lt;/p&gt;

&lt;p&gt;Core design principles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Local-first&lt;/strong&gt;: cache, vector index, embedding model, and reranker all run locally (&lt;code&gt;~/.wigolo/&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No forced API keys&lt;/strong&gt;: the six core tools — search, fetch, crawl, extract, cache, find_similar — require zero API keys&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explicit failure&lt;/strong&gt;: bot blocks, dead engines, and stale cache hits are reported in results rather than silently dropped or disguised as empty results&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project Info
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Author&lt;/strong&gt;: KnockOutEZ&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language&lt;/strong&gt;: TypeScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License&lt;/strong&gt;: AGPL-3.0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Status&lt;/strong&gt;: Public Beta&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Website&lt;/strong&gt;: knockoutez.github.io/wigolo&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project Stats
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⭐ GitHub Stars: &lt;strong&gt;1,218+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🍴 Forks: 83+&lt;/li&gt;
&lt;li&gt;📄 License: AGPL-3.0&lt;/li&gt;
&lt;li&gt;📅 Created: 2026-04-12&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Quick Setup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# One command: download browser engine and local models, write MCP config&lt;/span&gt;
npx wigolo init &lt;span class="nt"&gt;--agents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;claude-code

&lt;span class="c"&gt;# Configure multiple agents at once&lt;/span&gt;
npx wigolo init &lt;span class="nt"&gt;--agents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;claude-code,cursor,codex

&lt;span class="c"&gt;# Check all component health&lt;/span&gt;
npx wigolo doctor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Requires Node ≥ 20 and about 1.5 GB of disk space (browser engine + local ML models).&lt;/p&gt;

&lt;p&gt;All six core tools work immediately after setup — no API keys needed. If you want &lt;code&gt;research&lt;/code&gt; and &lt;code&gt;agent&lt;/code&gt; tools to produce a synthesized answer (rather than raw data for the host LLM to process), a free Gemini key is the single biggest quality upgrade:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;WIGOLO_LLM_PROVIDER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gemini
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;free key from aistudio.google.com&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For fully air-gapped operation, use a local Ollama model instead: &lt;code&gt;WIGOLO_LLM_PROVIDER=ollama&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  10 Tools, Explained
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Core Retrieval Tools
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;search&lt;/code&gt;&lt;/strong&gt; — Multi-engine parallel search&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;18 direct engine adapters (no intermediate proxy API)&lt;/li&gt;
&lt;li&gt;Rank fusion across engines + ML reranking&lt;/li&gt;
&lt;li&gt;Pass an array of queries to fan out multiple searches in a single MCP call&lt;/li&gt;
&lt;li&gt;Each result includes an explainable score: &lt;code&gt;semantic&lt;/code&gt; + &lt;code&gt;lexical&lt;/code&gt; + &lt;code&gt;engine_consensus&lt;/code&gt; broken out separately&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;fetch&lt;/code&gt;&lt;/strong&gt; — Tiered-routing page retrieval&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plain HTTP request
    ↓ (fails or detects SPA signals)
Headless browser (handles JS-rendered content)
    ↓ (encounters bot challenge)
Challenge clearance flow
    ↓ (clearance fails)
Flags blocked_by_challenge — does NOT pretend it succeeded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;wigolo learns which tier each domain requires and skips unnecessary escalations on repeat visits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;crawl&lt;/code&gt;&lt;/strong&gt; — Multi-page crawling&lt;/p&gt;

&lt;p&gt;Supports BFS, DFS, sitemap mode, or map-only (just build the site map, don't download content). Respects robots.txt, enforces per-domain rate limits, and deduplicates boilerplate templates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;extract&lt;/code&gt;&lt;/strong&gt; — Structured extraction&lt;/p&gt;

&lt;p&gt;Extracts from a page: tables, JSON-LD, metadata, brand assets, named schemas (Article / Recipe / Product / …), or any arbitrary structure via a custom JSON Schema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory and Discovery Tools
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;cache&lt;/code&gt;&lt;/strong&gt; — Local semantic cache&lt;/p&gt;

&lt;p&gt;Every visited page is automatically cached. Supports both keyword and semantic similarity queries, works offline. Also exposes cache stats, clear, and change detection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;find_similar&lt;/code&gt;&lt;/strong&gt; — Similar content discovery&lt;/p&gt;

&lt;p&gt;Find pages similar to a given URL or concept, via three-way fusion: keyword + semantic + live web.&lt;/p&gt;

&lt;h3&gt;
  
  
  Research and Autonomous Tools
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;research&lt;/code&gt;&lt;/strong&gt; — Deep research&lt;/p&gt;

&lt;p&gt;Decomposes a question → fires parallel sub-queries → fetches sources → synthesizes a cited report (or generates a structured summary for the host LLM to write).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;agent&lt;/code&gt;&lt;/strong&gt; — Autonomous gather loop&lt;/p&gt;

&lt;p&gt;Self-plans → searches → fetches → extracts → synthesizes, with configurable time budget and optional output JSON Schema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitoring Tools
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;diff&lt;/code&gt;&lt;/strong&gt; + &lt;strong&gt;&lt;code&gt;watch&lt;/code&gt;&lt;/strong&gt; — Page change detection&lt;/p&gt;

&lt;p&gt;Check what changed on a URL since the last visit; set up recurring monitoring and push changes to a webhook.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deep Dive: Two Unique Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Byte-Pinned Source Provenance
&lt;/h3&gt;

&lt;p&gt;This is wigolo's most distinctive technical feature — one that no other tool in the comparison set offers. Each search result carries not just a snippet, but:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"excerpt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Logical replication is a method of replicating data objects…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"citation_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source_span"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1042&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"end"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1305&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"final"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.86&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"semantic"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.91&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lexical"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.78&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"engine_consensus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;source_span&lt;/code&gt; is a byte-offset range pinning the excerpt to its exact location in the original document — not "somewhere in this article," but a precise, verifiable position.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;evidence_score&lt;/code&gt; breaks down into three independently interpretable components: semantic relevance, lexical match, and engine consensus (how many engines returned this result). Weak results are flagged as junk by wigolo's own scorer and surfaced in the output rather than silently filtered.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it matters&lt;/strong&gt;: When an AI agent builds a RAG pipeline or answers questions that need citations, byte-level provenance means references are actually verifiable rather than plausibly attributed. The "AI might be fabricating citation locations" uncertainty drops by one layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Local-First Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI agent (any MCP client / REST / SDK)
    ↓
wigolo process (single Node process)
    │
    ├── Search layer
    │   18 direct engine adapters
    │   rank fusion + ML reranking (local model)
    │
    ├── Fetch layer
    │   Tiered routing (HTTP → headless browser → challenge clearance)
    │   Per-domain learning + cache reuse
    │
    ├── Local storage (~/.wigolo/)
    │   Keyword index + vector index (local embedding model)
    │   Browser engine + ML models
    │
    └── Optional cloud (dashed)
        LLM API (only for research/agent synthesis)
        Aggregator backend (widen search funnel, legacy mode)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What runs locally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Direct search engine adapters&lt;/li&gt;
&lt;li&gt;✅ ML reranking model&lt;/li&gt;
&lt;li&gt;✅ Vector embedding model (&lt;code&gt;all-MiniLM-L6-v2&lt;/code&gt; etc.)&lt;/li&gt;
&lt;li&gt;✅ Headless browser engine&lt;/li&gt;
&lt;li&gt;✅ Local cache (SQLite + vector index)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What's optionally cloud:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔲 LLM synthesis (when &lt;code&gt;research&lt;/code&gt;/&lt;code&gt;agent&lt;/code&gt; needs a synthesized answer)&lt;/li&gt;
&lt;li&gt;🔲 Aggregator backend (widen the search funnel, optional)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Head-to-Head Comparison: wigolo vs Tavily / Exa / Firecrawl
&lt;/h2&gt;

&lt;p&gt;This is the main event. wigolo's README includes a comparison table, but stops short of explaining each tool's actual positioning and optimal use case. Here's a deeper reading.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feature Matrix
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;wigolo&lt;/th&gt;
&lt;th&gt;Tavily&lt;/th&gt;
&lt;th&gt;Exa&lt;/th&gt;
&lt;th&gt;Firecrawl&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Multi-engine web search&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page fetch + structured extract&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full-site crawl&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Byte-pinned source provenance&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Explainable score decomposition&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Persistent local cache (offline re-query)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query data stays on your machine&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API key / account required&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Required&lt;/td&gt;
&lt;td&gt;Required&lt;/td&gt;
&lt;td&gt;Required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per-query cost&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;Metered&lt;/td&gt;
&lt;td&gt;Metered&lt;/td&gt;
&lt;td&gt;Metered&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Each Tool's Actual Positioning
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Tavily&lt;/strong&gt; — Purpose-built search API for AI agents; currently the most widely integrated paid service in the "RAG + agent search" space. Strengths: production-grade quality out of the box, native support in LangChain/LlamaIndex and most major frameworks, search quality tuned specifically for agent use cases. Weakness: per-query billing, which can add up quickly in high-frequency agent workflows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exa&lt;/strong&gt; — Positions itself as "semantic web search," designed to understand the intent behind a query rather than just matching keywords. Notable strength: high-quality retrieval of technical documentation and academic content — the benchmark section in wigolo's README notes that Exa successfully rendered the full comparison matrix from an official docs page that other tools missed. Like Tavily: requires registration + metered billing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Firecrawl&lt;/strong&gt; — Focused on web crawling and structured extraction, not search. Primary strength: handling complex JS-rendered pages, large-scale crawl jobs, and extracting structured data from arbitrary websites. The right tool for "given a set of URLs, batch-extract structured content" — not for "given a question, search for relevant content."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;wigolo&lt;/strong&gt; — Attempts to combine all three capabilities into a single local tool while eliminating API keys and per-query costs. The differentiated bets: byte-pinned provenance, explainable scores, local cache with offline re-query capability, and $0 core tooling cost.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario Recommendations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Choose wigolo when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frequent technical documentation queries during development (no billing constraints)&lt;/li&gt;
&lt;li&gt;Source traceability matters (byte-level pinning → agent citations are verifiable)&lt;/li&gt;
&lt;li&gt;Privacy requirements prohibit query data leaving the machine&lt;/li&gt;
&lt;li&gt;High agent query volume makes cloud API costs a concern&lt;/li&gt;
&lt;li&gt;Building "memory-enabled" research tasks that benefit from local caching across sessions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choose Tavily when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Production-grade quality out of the box, no local dependencies to maintain&lt;/li&gt;
&lt;li&gt;Already in the LangChain/LlamaIndex ecosystem needing framework-native integration&lt;/li&gt;
&lt;li&gt;Query volume is manageable and per-query billing is acceptable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choose Exa when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Primary use case is technical documentation or academic content (semantic relevance priority)&lt;/li&gt;
&lt;li&gt;Official documentation retrieval quality is critical (strong docs page rendering)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choose Firecrawl when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Batch crawling specific websites to extract structured content (not a search use case)&lt;/li&gt;
&lt;li&gt;Large-scale web content processing pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  An Honest Limitation
&lt;/h3&gt;

&lt;p&gt;wigolo's README flags one: data-center IP reputation scores lower than residential IPs on some sites' bot-detection systems, which can reduce challenge-clearance rates when running on a cloud server vs. a local machine. Self-hosted deployments should account for this; the README documents optional proxy configurations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Other Integration Options
&lt;/h2&gt;

&lt;p&gt;wigolo isn't just a Claude Code plugin. It supports multiple invocation paths:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;REST API&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wigolo serve   &lt;span class="c"&gt;# starts on 127.0.0.1:3333&lt;/span&gt;

curl &lt;span class="nt"&gt;-sX&lt;/span&gt; POST http://127.0.0.1:3333/v1/search &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"query":"local-first software","max_results":5}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cross-loopback connections require a bearer token; fail-closed by design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TypeScript / Python SDKs&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;wigolo&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;local_client&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;local_client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;local-first web search&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_results&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;results&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDK automatically reuses a running daemon, or starts one if needed, and shuts it down as appropriate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Framework Integrations&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Framework&lt;/th&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;What it exposes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;LangChain&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wigolo-langchain&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Each tool as &lt;code&gt;BaseTool&lt;/code&gt; + &lt;code&gt;BaseRetriever&lt;/code&gt; for RAG&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CrewAI&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wigolo-crewai&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;wigolo_tools()&lt;/code&gt; passed directly to a crew&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LlamaIndex&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wigolo-llamaindex&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;BaseReader&lt;/code&gt; loading pages as Documents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vercel AI SDK&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wigolo-vercel-ai-sdk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tool factory for &lt;code&gt;generateText&lt;/code&gt;/&lt;code&gt;streamText&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Official Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🌟 &lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/KnockOutEZ/wigolo" rel="noopener noreferrer"&gt;KnockOutEZ/wigolo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🌐 &lt;strong&gt;Website&lt;/strong&gt;: &lt;a href="https://knockoutez.github.io/wigolo/" rel="noopener noreferrer"&gt;knockoutez.github.io/wigolo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;npm&lt;/strong&gt;: &lt;a href="https://www.npmjs.com/package/wigolo" rel="noopener noreferrer"&gt;wigolo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📄 &lt;strong&gt;Docs&lt;/strong&gt;: &lt;a href="https://github.com/KnockOutEZ/wigolo/blob/main/docs/README.md" rel="noopener noreferrer"&gt;docs/README.md&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;wigolo's core thesis: an AI agent's web access layer shouldn't be a perpetually-billed cloud API black box. Search engine raw data is public. Reranking and extraction algorithms can run locally. Query history can be cached into a local semantic index that persists across sessions. Put these together and you get a zero-cost, on-device, offline-capable web intelligence layer.&lt;/p&gt;

&lt;p&gt;Two design decisions are worth singling out:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Byte-pinned source provenance&lt;/strong&gt;: Unique among all the tools compared. When an agent cites a search result, &lt;code&gt;source_span&lt;/code&gt; provides a byte-offset range that ties the excerpt to its exact position in the source document. For research tasks that require verifiable citations, this removes one layer of "the AI might be fabricating where this came from" uncertainty.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explicit failure over silent pretense&lt;/strong&gt;: Bot blocks surface as &lt;code&gt;blocked_by_challenge&lt;/code&gt;. Dead engines report themselves. Weak results are scored and flagged by wigolo's own evaluator, then shown in the output rather than quietly discarded. This "always tell you what ground you're standing on" philosophy is significantly more reliable in agent contexts than tools that present silence or empty results when something goes wrong.&lt;/p&gt;

&lt;p&gt;If your Claude Code workflow involves heavy documentation querying, technical information search, or if you're building an agent pipeline that needs web search — &lt;code&gt;npx wigolo init --agents=claude-code&lt;/code&gt; is currently the lowest-cost entry point in the most literal sense: zero dollars, zero API keys, zero per-query billing.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Explore &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — a curated marketplace of AI agents and skills, each validated against real enterprise workflows. No hype, just what actually works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Visit my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;personal site&lt;/a&gt; for more insights and interesting products.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>agents</category>
      <category>crawler</category>
      <category>claude</category>
    </item>
    <item>
      <title>Open Source Project #126: code-review-graph — Build a Structural Map of Your Codebase to Cut AI Review Tokens by 82x</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Sat, 18 Jul 2026 01:49:07 +0000</pubDate>
      <link>https://dev.to/wonderlab/open-source-project-126-code-review-graph-build-a-structural-map-of-your-codebase-to-cut-ai-3jgl</link>
      <guid>https://dev.to/wonderlab/open-source-project-126-code-review-graph-build-a-structural-map-of-your-codebase-to-cut-ai-3jgl</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"Stop burning tokens. Start reviewing smarter."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is &lt;strong&gt;article #126&lt;/strong&gt; in the "One Open Source Project a Day" series. Today's project is &lt;strong&gt;code-review-graph&lt;/strong&gt; — a local-first tool that builds a persistent structural map of your codebase and delivers precise context to AI coding tools via MCP.&lt;/p&gt;

&lt;p&gt;AI coding agents have a hidden inefficiency during code reviews: they don't know "if I change this function, what else breaks?" — so they either read large portions of the codebase to build context, or rely on grep and hope for the best. code-review-graph's solution: pre-build a structural graph (functions, classes, call edges, inheritance, test coverage), then at review time query the graph to compute the "blast radius" of a change, and give the AI only the files that actually matter.&lt;/p&gt;

&lt;p&gt;Benchmark: ~82x median token reduction. 528x best case.&lt;/p&gt;

&lt;p&gt;19,762 Stars. Created February 2026. Supports 14 AI coding platforms.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You'll Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;How blast-radius analysis works: tracing from changed files to callers, dependents, and tests&lt;/li&gt;
&lt;li&gt;The full architecture: Tree-sitter AST → SQLite graph → MCP tool chain&lt;/li&gt;
&lt;li&gt;The incremental update mechanism: why a 2,900-file repo re-indexes in under 2 seconds&lt;/li&gt;
&lt;li&gt;Three-tier edge confidence scoring (EXTRACTED/INFERRED/AMBIGUOUS) and what it means&lt;/li&gt;
&lt;li&gt;An honest reading of the benchmark numbers: 528x is the best case, not the median&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Basic experience with Claude Code or other AI coding tools&lt;/li&gt;
&lt;li&gt;Familiarity with MCP (Model Context Protocol)&lt;/li&gt;
&lt;li&gt;Basic understanding of static analysis and call graphs&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Project Background
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Overview
&lt;/h3&gt;

&lt;p&gt;code-review-graph (CRG) is a local-first code intelligence tool with four core functions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parse your codebase with Tree-sitter into a graph of functions, classes, imports, and call relationships&lt;/li&gt;
&lt;li&gt;Persist the graph as a SQLite file in &lt;code&gt;.code-review-graph/&lt;/code&gt; — zero network dependencies&lt;/li&gt;
&lt;li&gt;Expose 30 MCP tools so your AI assistant can query "what does this change affect?"&lt;/li&gt;
&lt;li&gt;Support incremental updates — on a change, re-parse only the modified files&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Author / Team
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Author&lt;/strong&gt;: tirth8205&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language&lt;/strong&gt;: Python 3.10+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License&lt;/strong&gt;: MIT&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version&lt;/strong&gt;: v2.3.6&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Website&lt;/strong&gt;: code-review-graph.com&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project Stats
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⭐ GitHub Stars: &lt;strong&gt;19,762+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🍴 Forks: 2,107+&lt;/li&gt;
&lt;li&gt;📄 License: MIT&lt;/li&gt;
&lt;li&gt;📅 Created: February 26, 2026&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Quick Start
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;code-review-graph        &lt;span class="c"&gt;# or: pipx install code-review-graph&lt;/span&gt;
code-review-graph &lt;span class="nb"&gt;install&lt;/span&gt;            &lt;span class="c"&gt;# auto-detects platforms, writes MCP configs&lt;/span&gt;
code-review-graph build              &lt;span class="c"&gt;# parse codebase, build graph&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three commands. &lt;code&gt;install&lt;/code&gt; detects every AI coding tool on your machine, writes the correct MCP configuration for each, injects graph-aware instructions into your platform rules files, and installs platform-native hooks/skills. Restart your editor, then ask the AI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build the code review graph for this project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initial build: ~10 seconds for a 500-file project. After that, incremental.&lt;/p&gt;

&lt;h3&gt;
  
  
  Platform Support
&lt;/h3&gt;

&lt;p&gt;One install, 14 platforms: Codex, Claude Code, CodeBuddy Code, Cursor, Windsurf, Zed, Continue, OpenCode, Antigravity, Gemini CLI, Qwen, Qoder, Kiro, GitHub Copilot.&lt;/p&gt;

&lt;p&gt;Target-specific installs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;code-review-graph &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--platform&lt;/span&gt; claude-code
code-review-graph &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--platform&lt;/span&gt; cursor
code-review-graph &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--platform&lt;/span&gt; codex
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Token Savings Panel
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────── Token Savings ────────────────────────┐
│ Full context would be:     12,921 tokens                     │
│ Graph context used:           762 tokens                     │
│ Saved:                     12,159 tokens (~94%)              │
│ Breakdown: Functions 244 · Tests 191 · Risk 244 · Other 83   │
└──────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Printed by &lt;code&gt;detect-changes --brief&lt;/code&gt; or &lt;code&gt;update --brief&lt;/code&gt;. Add &lt;code&gt;--verify&lt;/code&gt; to cross-check against OpenAI's &lt;code&gt;cl100k_base&lt;/code&gt; tokenizer; the estimate stays within ~1% of real tokens in practice.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deep Dive
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Core Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Repository
    │
    ▼ git ls-files (only tracked files indexed)
Tree-sitter parsers
    │ extracts: functions / classes / imports / calls / inheritance / test coverage
    ▼
SQLite graph database
(.code-review-graph/, git-ignored by default)
    │
    ├── MCP server (30 tools)
    │       ↓ AI assistant queries
    │   get_impact_radius_tool
    │   get_review_context_tool
    │   detect_changes_tool
    │   ...
    │
    └── CLI
        detect-changes --brief
        update --brief
        visualize
        ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Graph nodes: functions, classes, files, modules&lt;br&gt;
Graph edges: call relationships, import relationships, inheritance, test coverage&lt;/p&gt;
&lt;h3&gt;
  
  
  Blast-Radius Analysis
&lt;/h3&gt;

&lt;p&gt;The core concept. When a file changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Changed file (e.g. login() in auth/login.py)
    ↓
Query graph: which functions directly call login()?
    ↓
Query graph: which functions call those callers?
            (configurable depth; default depth=2)
    ↓
Query graph: which test files cover these functions?
    ↓
Output: blast radius = minimal set of functions/files affected
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI reads only this minimal set instead of scanning the whole codebase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accuracy note (from the README, unusually candid):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The current recall=1.0 is a &lt;strong&gt;graph-derived upper bound&lt;/strong&gt; — the ground truth comes from the same graph edges the predictor walks, so the measurement is circular by construction&lt;/li&gt;
&lt;li&gt;"Co-change mode" grades against files the author actually co-edited in the same commit — independent evidence from git history, not the graph — and will produce substantially lower numbers&lt;/li&gt;
&lt;li&gt;Blast-radius analysis is deliberately conservative: it's better to flag extra files than to miss a broken dependency&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Incremental Updates: &amp;lt; 2 Seconds
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;File saved (hook fires / watch mode / crg-daemon)
    │
    ▼
SHA-256 hash check: which files actually changed?
    │
    ▼
Re-parse only changed files
    │
    ▼
Find dependents of changed files, mark edges for refresh
    │
    ▼
Graph updated

Benchmark: 2,900-file repo → &amp;lt; 2 seconds
           500-file repo, initial build → ~10 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Three-Tier Edge Confidence
&lt;/h3&gt;

&lt;p&gt;Graph edges carry confidence levels:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EXTRACTED&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Explicit call parsed directly from the AST — high confidence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;INFERRED&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Relationship derived via type inference or semantic analysis — medium confidence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AMBIGUOUS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dynamic dispatch, polymorphism, or cases that can't be statically resolved — low confidence&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This lets the AI filter by confidence when querying — avoiding false alerts from low-quality edges in dynamically-typed code.&lt;/p&gt;

&lt;h3&gt;
  
  
  30 MCP Tools
&lt;/h3&gt;

&lt;p&gt;Organized by purpose:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context retrieval (code review core)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;get_minimal_context_tool&lt;/code&gt; — ultra-compact context, ~100 tokens, call this first&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_impact_radius_tool&lt;/code&gt; — blast radius of changed files&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_review_context_tool&lt;/code&gt; — token-optimized review context with structural summary&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;detect_changes_tool&lt;/code&gt; — risk-scored change impact analysis&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Graph queries&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;query_graph_tool&lt;/code&gt; — query callers, callees, tests, imports, inheritance&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;traverse_graph_tool&lt;/code&gt; — free-form BFS/DFS from any node with token budget&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;semantic_search_nodes_tool&lt;/code&gt; — search code entities by name or meaning (requires optional embeddings)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Architecture analysis&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;get_architecture_overview_tool&lt;/code&gt; — auto-generated architecture map from community structure&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_hub_nodes_tool&lt;/code&gt; — most-connected nodes (architectural hotspots)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_bridge_nodes_tool&lt;/code&gt; — chokepoints via betweenness centrality&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_surprising_connections_tool&lt;/code&gt; — unexpected cross-community coupling&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_knowledge_gaps_tool&lt;/code&gt; — isolated nodes, untested hotspots, structural weaknesses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Other&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;refactor_tool&lt;/code&gt; — rename preview, dead code detection&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;generate_wiki_tool&lt;/code&gt; — Markdown wiki from community structure&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cross_repo_search_tool&lt;/code&gt; — search across all registered repos&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In token-constrained environments, expose only what you need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;code-review-graph serve &lt;span class="nt"&gt;--tools&lt;/span&gt; query_graph_tool,detect_changes_tool,get_review_context_tool
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Language Coverage
&lt;/h3&gt;

&lt;p&gt;40+ languages and formats: Python, JavaScript/TypeScript/TSX, Go, Rust, Java, C/C++, C#, Ruby, Kotlin, Swift, PHP, Scala, Solidity, Dart, R, Elixir, Zig, Vue/Svelte SFCs, Jupyter/Databricks notebooks (.ipynb), Terraform, Ansible, and more.&lt;/p&gt;

&lt;p&gt;For unsupported languages, drop a &lt;code&gt;languages.toml&lt;/code&gt; in &lt;code&gt;.code-review-graph/&lt;/code&gt; — no fork or code changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[languages.erlang]&lt;/span&gt;
&lt;span class="py"&gt;extensions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;".erl"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;grammar&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"erlang"&lt;/span&gt;
&lt;span class="py"&gt;function_node_types&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"function_clause"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;class_node_types&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"record_decl"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;import_node_types&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"import_attribute"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;call_node_types&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CI Integration: GitHub Action
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
  &lt;span class="na"&gt;pull-requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;review&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v7&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tirth8205/code-review-graph@v2.3.6&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;github-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Graph built and queried entirely on the CI runner. &lt;strong&gt;No source code sent to any external service.&lt;/strong&gt; Posts a sticky risk-scored comment on each PR, updated in place on every push. Optional &lt;code&gt;fail-on-risk&lt;/code&gt; input turns the review into a merge gate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-Repo Daemon
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;crg-daemon add ~/project-a &lt;span class="nt"&gt;--alias&lt;/span&gt; proj-a
crg-daemon add ~/project-b
crg-daemon start
crg-daemon status
crg-daemon stop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For editors that don't support hooks (Cursor, OpenCode, etc.). Watches multiple repos in the background, health-checks every 30 seconds, auto-restarts dead watchers. Config persists in &lt;code&gt;~/.code-review-graph/watch.toml&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benchmark Numbers — An Honest Reading
&lt;/h3&gt;

&lt;p&gt;The README handles the benchmark numbers with unusual candor:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Repo&lt;/th&gt;
&lt;th&gt;Corpus tokens&lt;/th&gt;
&lt;th&gt;Graph tokens&lt;/th&gt;
&lt;th&gt;Reduction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;fastapi&lt;/td&gt;
&lt;td&gt;951,071&lt;/td&gt;
&lt;td&gt;2,169&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;528x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;code-review-graph&lt;/td&gt;
&lt;td&gt;208,821&lt;/td&gt;
&lt;td&gt;2,495&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;93x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gin&lt;/td&gt;
&lt;td&gt;166,868&lt;/td&gt;
&lt;td&gt;1,990&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;92x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flask&lt;/td&gt;
&lt;td&gt;125,022&lt;/td&gt;
&lt;td&gt;1,986&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;71x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;express&lt;/td&gt;
&lt;td&gt;135,955&lt;/td&gt;
&lt;td&gt;3,465&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;41x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;httpx&lt;/td&gt;
&lt;td&gt;89,492&lt;/td&gt;
&lt;td&gt;2,438&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;38x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Median&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~82x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;528x is the best case (fastapi, the largest corpus). The median is 82x.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The whole-corpus baseline is an upper bound no real agent pays: a competent agent would grep for identifiers and read only the best-matching files. The &lt;code&gt;agent_baseline&lt;/code&gt; benchmark measures this more realistic baseline — pure-python grep, top-3 files by match count — and that comparison is the honest one.&lt;/p&gt;

&lt;p&gt;For small single-file changes, graph context can &lt;em&gt;exceed&lt;/em&gt; naive file reads (the structural metadata overhead is larger than the file content). The express results show this. The README calls it out rather than hiding it.&lt;/p&gt;

&lt;h3&gt;
  
  
  vs. Related Tools
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;CRG difference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;LSP / language servers&lt;/td&gt;
&lt;td&gt;Per-language, per-symbol precision&lt;/td&gt;
&lt;td&gt;CRG: one persistent cross-language graph; LSP stays more precise per symbol&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAG / embeddings&lt;/td&gt;
&lt;td&gt;Similarity chunks&lt;/td&gt;
&lt;td&gt;CRG: structural edges from AST parsing; embeddings are optional and only assist search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;grep / agentic search&lt;/td&gt;
&lt;td&gt;One-hop lookups&lt;/td&gt;
&lt;td&gt;CRG wins on multi-hop questions: impact radius, callers-of-callers, tests-for, affected flows&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When NOT to use CRG: small repos, trivial single-file diffs, one-off questions. The overhead of maintaining the graph isn't worth it for these cases.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Official Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🌟 &lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/tirth8205/code-review-graph" rel="noopener noreferrer"&gt;tirth8205/code-review-graph&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🌐 &lt;strong&gt;Website&lt;/strong&gt;: &lt;a href="https://code-review-graph.com" rel="noopener noreferrer"&gt;code-review-graph.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;PyPI&lt;/strong&gt;: &lt;a href="https://pypi.org/project/code-review-graph/" rel="noopener noreferrer"&gt;code-review-graph&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📄 &lt;strong&gt;Benchmark reproduction&lt;/strong&gt;: &lt;a href="https://github.com/tirth8205/code-review-graph/blob/main/docs/REPRODUCING.md" rel="noopener noreferrer"&gt;docs/REPRODUCING.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💬 &lt;strong&gt;Discord&lt;/strong&gt;: &lt;a href="https://discord.gg/3p58KXqGFN" rel="noopener noreferrer"&gt;discord.gg/3p58KXqGFN&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;code-review-graph addresses a problem that gets worse as codebases grow: an AI agent doesn't know that changing A affects B, so it either reads too many files or relies on grep. CRG pre-builds the "what affects what" map so agents can query it directly.&lt;/p&gt;

&lt;p&gt;Three engineering decisions worth noting:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Local-first with zero telemetry&lt;/strong&gt;: the graph lives in SQLite, cloud embeddings are opt-in (off by default), and the GitHub Action runs entirely on your CI runner. Source code stays on your machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Honest benchmarks&lt;/strong&gt;: 528x is labeled "best case," the median 82x is the headline; recall=1.0 is explicitly called out as circular (graph-derived ground truth); small-commit overhead is acknowledged rather than hidden. This level of transparency is genuinely uncommon in developer tooling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Incremental updates as the primary path&lt;/strong&gt;: the initial build is a one-time cost. Two-second incremental updates mean hooks and watch mode can keep the graph current as you work, not just when you remember to rebuild.&lt;/p&gt;

&lt;p&gt;For medium-to-large codebases (hundreds to thousands of files), frequent code review workflows, or teams that regularly ask "if I change this, what breaks?" — CRG's MCP integration path is currently one of the most direct available. Three commands to onboard, then the agent queries the graph automatically from there.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Explore &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — a curated marketplace of AI agents and skills, each validated against real enterprise workflows. No hype, just what actually works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Visit my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;personal site&lt;/a&gt; for more insights and interesting products.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>mcp</category>
      <category>knowledgebase</category>
    </item>
    <item>
      <title>AI Evaluation Series (01): Why AI Evaluation Is Hard — Uncertainty, Subjectivity, and Multiple Dimensions</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Sat, 18 Jul 2026 01:35:10 +0000</pubDate>
      <link>https://dev.to/wonderlab/ai-evaluation-series-01-why-ai-evaluation-is-hard-uncertainty-subjectivity-and-multiple-2p04</link>
      <guid>https://dev.to/wonderlab/ai-evaluation-series-01-why-ai-evaluation-is-hard-uncertainty-subjectivity-and-multiple-2p04</guid>
      <description>&lt;h2&gt;
  
  
  What Traditional Software Testing Assumes
&lt;/h2&gt;

&lt;p&gt;Traditional software testing rests on three assumptions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic output&lt;/strong&gt;: same input gives same output; tests are reproducible&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correctness is decidable&lt;/strong&gt;: a function returns 42 — right or wrong, there's a clear standard&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single dimension&lt;/strong&gt;: code either passes the test or it doesn't; no "kind of passes"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;AI applications break all three.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three Fundamental Problems
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Problem 1: Non-Deterministic Output
&lt;/h3&gt;

&lt;p&gt;The same Prompt gets different responses from Claude today versus tomorrow. Setting &lt;code&gt;temperature=0&lt;/code&gt; reduces randomness but doesn't eliminate it. Model updates silently change outputs for the same input.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Engineering implication:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Traditional test
&lt;/span&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;calculator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;  &lt;span class="c1"&gt;# runs 100 times, passes 100 times
&lt;/span&gt;
&lt;span class="c1"&gt;# AI evaluation
&lt;/span&gt;&lt;span class="n"&gt;score1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;judge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Explain the Transformer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# 4.2/5
&lt;/span&gt;&lt;span class="n"&gt;score2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;judge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Explain the Transformer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# 3.8/5
# 0.4 point gap — normal variance or a problem?
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample multiple times and average. Three to five samples is usually enough; cost is 3-5x. Don't judge quality from a single run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 2: Subjective Quality
&lt;/h3&gt;

&lt;p&gt;"Explain what a Transformer is" has countless correct answers. An explanation for a 10-year-old and one for an ML engineer can both be "good answers" while being completely different.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Subjectivity shows up as:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Same question, different good answers for different audiences
  → Depth: beginner vs expert
  → Style: analogy vs equations
  → Length: 100 words vs 2000

Same answer, different scores for different goals
  → Goal is "quick understanding" → short, clear answer scores high
  → Goal is "deep learning" → comprehensive answer scores high
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fix: &lt;strong&gt;define the scenario and audience before evaluating&lt;/strong&gt;. Convert subjective judgment into a concrete scoring Rubric so LLM-as-Judge has clear criteria to follow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 3: Multi-Dimensional Quality
&lt;/h3&gt;

&lt;p&gt;"AI responses should be good" unpacks into multiple independent dimensions that a single score can't capture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Accuracy:     content matches facts, no hallucinations
Relevance:    the response addresses the user's actual question
Completeness: covers the core aspects of the question
Clarity:      well-organized, easy to follow
Helpfulness:  the user can actually act on this response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A response can be accurate but incomplete, or complete but unclear. Compressing 5 dimensions into 1 number loses too much information.&lt;/p&gt;




&lt;h2&gt;
  
  
  Four Evaluation Methods
&lt;/h2&gt;

&lt;p&gt;No evaluation method works for every situation. Each carries costs. The choice depends on the scenario and budget.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Method            Accuracy   Speed      Cost   Best for
──────────────────────────────────────────────────────────────
Human evaluation  Highest    Slow (days) High   Baseline setup, periodic sampling
Rule/auto metrics Medium     Fast (ms)   Low    Format checks, keyword recall
LLM-as-Judge      High       Medium (min) Med   Main workhorse; needs bias mitigation
A/B testing       High       Slow (weeks) High  Production with real user behavior
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Human Evaluation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt; Highest accuracy, captures subtle quality differences, serves as the baseline for all other methods.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt; Slow, expensive, doesn't scale. An evaluator reviews 50-100 samples per day; 1000 samples takes two weeks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to use:&lt;/strong&gt; Build a "gold test set" for each critical scenario (about 100 samples, human-labeled), and use it as the reference standard. Validate automatic evaluation methods by comparing their results against the gold set.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rule / Auto Metrics
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt; Millisecond speed, near-zero cost, easy to integrate into CI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt; Limited coverage; can't assess semantic quality. BLEU/ROUGE measure surface-level word overlap, not whether the meaning is correct.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Useful automatic metrics
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Does the output conform to the JSON Schema?&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_words&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_words&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Is the length in the expected range?&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_no_hallucination_markers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Does the output contain known hallucination phrases?&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="c1"&gt;# Not useful for semantic quality
&lt;/span&gt;&lt;span class="n"&gt;rouge_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rouge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;predictions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;references&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="c1"&gt;# ROUGE penalizes semantically equivalent rephrasings and rewards verbatim repetition
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How to use:&lt;/strong&gt; Layer 2 structural checks — format compliance, length validation, prohibited phrase filtering. Don't use for semantic quality judgment.&lt;/p&gt;

&lt;h3&gt;
  
  
  LLM-as-Judge
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt; Scales well, understands semantics, evaluates quality dimensions that require reasoning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt; Susceptible to biases (covered in detail in Article 03), costs about 1/10th of human evaluation but 100x more than rule methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;JUDGE_PROMPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Evaluate the following AI response (1-5 per dimension):

Dimensions:
1. Accuracy: Is the content factually correct?
2. Relevance: Does it address the user&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s question?
3. Clarity: Is it easy to understand?

User question: {question}
AI response: {answer}

Return as JSON: {{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accuracy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: int, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;relevance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: int, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;clarity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: int}}&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How to use:&lt;/strong&gt; The main method for semantic quality dimensions that automatic metrics can't cover.&lt;/p&gt;

&lt;h3&gt;
  
  
  A/B Testing
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt; Tests actual user behavior, closest to business value. User clicks, adoption, and conversion are more trustworthy than model self-assessment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt; Requires sufficient traffic (statistical significance typically needs hundreds of exposures), takes weeks, and requires online experiment infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to use:&lt;/strong&gt; Validate large changes (new model version, Prompt rewrite) for business impact. Not suitable for rapid iteration during development.&lt;/p&gt;




&lt;h2&gt;
  
  
  Combining Methods by Layer
&lt;/h2&gt;

&lt;p&gt;In practice, the three approaches compose by layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Development (rapid iteration):
  Rule checks (automatic) + LLM-as-Judge (sampling)
  → CI runs format checks on every commit
  → Daily sampling quality evaluation

Release (quality gate):
  LLM-as-Judge (full or large sample) + human spot-check of critical cases
  → Release only when quality score exceeds threshold

Production (continuous monitoring):
  A/B testing + real-time rule checks + periodic human sampling
  → Monitor quality drift, surface systemic problems
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The determinism trap&lt;/strong&gt;: traditional "assert" thinking doesn't work for AI evaluation — multiple samples with statistics is more reliable than a single assertion&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subjectivity isn't a blocker&lt;/strong&gt;: subjective quality becomes operational through clear scenario definitions, audience specifications, and scoring rubrics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No single best method&lt;/strong&gt;: rules are fast but narrow in coverage; human evaluation is accurate but expensive; LLM-as-Judge is the best cost-effectiveness ratio for the main workload; A/B testing provides the final commercial validation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The next article goes into practice: how to decompose the vague goal "AI responses should be good" into a measurable L1/L2/L3 three-layer metric system.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Check out &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — a curated marketplace of AI agents and skills that have been validated in real-world, enterprise-grade workflows. No fluff, just what actually works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Find more useful knowledge and interesting products on my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;Homepage&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>evaluation</category>
      <category>engineering</category>
      <category>llm</category>
    </item>
    <item>
      <title>MCP Series (08): Enterprise Governance — Registry, Routing, and Observability</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Fri, 17 Jul 2026 02:07:28 +0000</pubDate>
      <link>https://dev.to/wonderlab/mcp-series-08-enterprise-governance-registry-routing-and-observability-664</link>
      <guid>https://dev.to/wonderlab/mcp-series-08-enterprise-governance-registry-routing-and-observability-664</guid>
      <description>&lt;h2&gt;
  
  
  Why Governance Becomes Necessary
&lt;/h2&gt;

&lt;p&gt;Three MCP Servers are manageable from memory. Twenty require a system.&lt;/p&gt;

&lt;p&gt;As the number of MCP Servers in an enterprise grows, predictable problems emerge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A new engineer builds a Jira tool that already exists because there's no directory&lt;/li&gt;
&lt;li&gt;An Agent calls the deprecated &lt;code&gt;search_jira&lt;/code&gt; tool (v1.x) instead of the current &lt;code&gt;search_issues&lt;/code&gt; (v2.x)&lt;/li&gt;
&lt;li&gt;A tool call fails with no log — unclear whether the Server crashed or the arguments were wrong&lt;/li&gt;
&lt;li&gt;Token costs spike with no visibility into which Server's which tool caused it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Registry solves discovery. Routing solves dispatch. Observability solves diagnosis.&lt;/p&gt;




&lt;h2&gt;
  
  
  MCP Registry
&lt;/h2&gt;

&lt;p&gt;A Registry is the enterprise directory of MCP Servers: their locations, versions, capabilities, and owners.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# mcp-registry.yaml&lt;/span&gt;
&lt;span class="na"&gt;servers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jira-tools&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Jira Tools&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Search,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;create,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;update&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Jira&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tickets"&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2.1.0"&lt;/span&gt;
    &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;engineering&lt;/span&gt;
    &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;@team-platform"&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;active&lt;/span&gt;
    &lt;span class="na"&gt;transport&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stdio&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python&lt;/span&gt;
    &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/opt/mcp/jira/server.py"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;capabilities&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;search_issues&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;create_issue&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;update_issue&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;jira&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;//projects&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;jira&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;//sprint/current&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;metrics&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;monthly_calls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4521&lt;/span&gt;
      &lt;span class="na"&gt;avg_latency_ms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;180&lt;/span&gt;
      &lt;span class="na"&gt;error_rate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.2%&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-tools&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GitHub Tools&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.5.0"&lt;/span&gt;
    &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;engineering&lt;/span&gt;
    &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;@team-platform"&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;active&lt;/span&gt;
    &lt;span class="na"&gt;transport&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stdio&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx&lt;/span&gt;
    &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-y"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;@modelcontextprotocol/server-github"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;capabilities&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;create_pull_request&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;search_repositories&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;get_file_contents&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jira-tools-legacy&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Jira Tools (Legacy)&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.2.0"&lt;/span&gt;
    &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;engineering&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deprecated&lt;/span&gt;
    &lt;span class="na"&gt;deprecation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Superseded&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;by&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;jira-tools&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;v2.x;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;search_jira&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;renamed&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;search_issues"&lt;/span&gt;
      &lt;span class="na"&gt;migration_guide&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Replace&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;search_jira&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;search_issues;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;argument&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;structure&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;unchanged"&lt;/span&gt;
      &lt;span class="na"&gt;removal_date&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2026-10-01"&lt;/span&gt;
    &lt;span class="na"&gt;capabilities&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;search_jira&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;create_jira_ticket&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Registry solves three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Discovery&lt;/strong&gt;: new Agents check the Registry to learn what's available — no relying on hallway conversations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deprecation signaling&lt;/strong&gt;: &lt;code&gt;deprecated&lt;/code&gt; status plus a &lt;code&gt;migration_guide&lt;/code&gt; gives Agent code a concrete migration path&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ownership&lt;/strong&gt;: every Server has an owner — when something breaks, you know who to contact&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Tool Routing Strategies
&lt;/h2&gt;

&lt;p&gt;When an Agent needs to "search Jira tickets," how does it find the right Server? Four strategies:&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy 1: Static Configuration (simplest)
&lt;/h3&gt;

&lt;p&gt;Declare all Servers directly in Agent settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"jira"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"python"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/opt/mcp/jira/server.py"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"github"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@modelcontextprotocol/server-github"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple and predictable. Adding a new Server requires updating every Agent config file manually.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy 2: Domain-Based Loading
&lt;/h3&gt;

&lt;p&gt;Load only the Servers relevant to the current task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;DOMAIN_SERVERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;engineering&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jira-tools&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;github-tools&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gitlab-tools&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgres-readonly&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bigquery-tools&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;communication&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;slack-tools&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email-tools&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_servers_for_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;classify_task_domain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;server_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DOMAIN_SERVERS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="n"&gt;registry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_registry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;servers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;server_ids&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;active&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reduces unnecessary Server startup overhead. Each Agent loads only the tools it needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy 3: Embedding Routing (semantic matching)
&lt;/h3&gt;

&lt;p&gt;The same approach as Skill Series Article 06 — embed Server descriptions, embed the user request, find the nearest neighbors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;route_to_server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;query_embedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;embedder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;scored&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;desc_embedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_cached_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cosine_similarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query_embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;desc_embedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;scored&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;scored&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;scored&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Good for 20+ Servers where new ones get added frequently. The same limitation applies as in Skill routing: Servers in the same domain cluster in embedding space. Use negative examples in descriptions to distinguish them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy 4: Hierarchical Routing (recommended)
&lt;/h3&gt;

&lt;p&gt;Coarse-filter by domain first, then run embedding matching within the domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hierarchical_route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="c1"&gt;# Layer 1: LLM classifies domain quickly
&lt;/span&gt;    &lt;span class="n"&gt;domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;llm_classify_domain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# "engineering" / "data" / ...
&lt;/span&gt;
    &lt;span class="c1"&gt;# Layer 2: embedding match within domain
&lt;/span&gt;    &lt;span class="n"&gt;domain_servers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;domain&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;embedding_route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;domain_servers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Observability: Langfuse Integration
&lt;/h2&gt;

&lt;p&gt;Without Trace, MCP tool calls are a black box. When something fails, you don't know why. When latency spikes, you don't know where. When token costs grow, you don't know which tool caused it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Three-Layer Trace Structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langfuse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Langfuse&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langfuse.decorators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;langfuse_context&lt;/span&gt;

&lt;span class="n"&gt;langfuse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Langfuse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mcp_tool_call&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;traced_tool_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;server_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;call_fn&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;langfuse_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_current_observation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arguments&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;server_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;server_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;server_version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;get_server_version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;t0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;call_fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;latency_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;t0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;

        &lt;span class="n"&gt;langfuse_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_current_observation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;latency_ms&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;latency_ms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;latency_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;t0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
        &lt;span class="n"&gt;langfuse_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_current_observation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt;
            &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;latency_ms&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;latency_ms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ERROR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Session-Level Trace
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent_session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_agent_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;langfuse_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_current_trace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user:alice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jira_query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;langfuse_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_current_observation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every tool call inside this session automatically attaches to the session trace.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Trace Answers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Which tool call is slowest?
  → sort Spans by latency_ms

Which Server has the highest error rate?
  → group by server_id, count success=False fraction

Where is token spend concentrated?
  → LLM Span usage field, grouped by tool_name

What caused a specific Agent session failure?
  → search by session_id in Langfuse, expand the trace tree
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Alert Rules
&lt;/h2&gt;

&lt;p&gt;Wire key metrics into your alerting system (Grafana / Prometheus):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Prometheus alerting rules&lt;/span&gt;
&lt;span class="na"&gt;groups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mcp_server_alerts&lt;/span&gt;
    &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;alert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MCPServerHighErrorRate&lt;/span&gt;
        &lt;span class="na"&gt;expr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mcp_tool_call_error_rate_5m &amp;gt; &lt;/span&gt;&lt;span class="m"&gt;0.05&lt;/span&gt;
        &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5m&lt;/span&gt;
        &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;warning&lt;/span&gt;
        &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MCP&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Server&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$labels.server_id&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;rate&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$value&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;humanizePercentage&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;alert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MCPToolHighLatency&lt;/span&gt;
        &lt;span class="na"&gt;expr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mcp_tool_call_p90_latency_ms &amp;gt; &lt;/span&gt;&lt;span class="m"&gt;5000&lt;/span&gt;
        &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3m&lt;/span&gt;
        &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;warning&lt;/span&gt;
        &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tool&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$labels.tool_name&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;P90&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;latency&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$value&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}ms"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;alert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DeprecatedToolInUse&lt;/span&gt;
        &lt;span class="na"&gt;expr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mcp_deprecated_tool_calls_total &amp;gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
        &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0m&lt;/span&gt;
        &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;info&lt;/span&gt;
        &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Deprecated&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tool&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$labels.tool_name&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;called&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$value&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;times&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;today"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Three-Level Governance Roadmap
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Level 1 — Individual/small team (do now):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create &lt;code&gt;mcp-registry.yaml&lt;/code&gt; with id / version / owner / status for every Server&lt;/li&gt;
&lt;li&gt;Each Server's README lists its tools and usage examples&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Level 2 — Team sharing (1-2 weeks):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Registry in Git, changes go through PR review&lt;/li&gt;
&lt;li&gt;Langfuse Trace on every tool call: log tool_name / latency / success at minimum&lt;/li&gt;
&lt;li&gt;Error rate alerts configured&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Level 3 — Enterprise governance (ongoing):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hierarchical routing (domain classification + embedding selection)&lt;/li&gt;
&lt;li&gt;Monthly Server health report (call volume, error rate, top consumers)&lt;/li&gt;
&lt;li&gt;Formal deprecation process (announce → alert → 90-day grace period → remove)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Registry is the foundation of discovery&lt;/strong&gt;: without it, 20 Servers rely on word-of-mouth, duplicate development and version confusion are inevitable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Match routing strategy to scale&lt;/strong&gt;: static config for 3 Servers; hierarchical routing (domain filter + embedding selection) for 20+, avoiding the same-domain embedding confusion problem&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability turns black boxes into records&lt;/strong&gt;: Langfuse's session-level + tool-call-level Trace means failure root causes go from 'unknown' to 'found in 5 seconds on the Dashboard'&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://langfuse.com/docs/integrations/other" rel="noopener noreferrer"&gt;Langfuse MCP Integration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;MCP Series planning document: &lt;a href="https://github.com/chendongqi/llm-in-action" rel="noopener noreferrer"&gt;MCP Knowledge Series Outline&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Check out &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — a curated marketplace of AI agents and skills that have been validated in real-world, enterprise-grade workflows. No fluff, just what actually works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Find more useful knowledge and interesting products on my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;Homepage&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>llm</category>
      <category>langfuse</category>
    </item>
    <item>
      <title>Open Source Project of the Day (#125): Open Interpreter — Open-Source AI Coding Agent, Rewritten in Rust, Supporting Kimi, Qwen, and DeepSeek</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Fri, 17 Jul 2026 02:06:09 +0000</pubDate>
      <link>https://dev.to/wonderlab/open-source-project-of-the-day-125-open-interpreter-open-source-ai-coding-agent-rewritten-in-2gk8</link>
      <guid>https://dev.to/wonderlab/open-source-project-of-the-day-125-open-interpreter-open-source-ai-coding-agent-rewritten-in-2gk8</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"A coding agent for open models — focused on getting the best performance out of low-cost models."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is article &lt;strong&gt;#125&lt;/strong&gt; in the &lt;em&gt;Open Source Project of the Day&lt;/em&gt; series. Today's project is &lt;strong&gt;Open Interpreter&lt;/strong&gt; — the 66k-Star open-source AI coding agent, now rewritten in Rust, and one of the most important local agent platforms in the open-model era.&lt;/p&gt;

&lt;p&gt;Open Interpreter's history has an interesting inflection point. It originally (2023) lit up GitHub as a Python project — one-line description: "run ChatGPT Code Interpreter on your local machine." The Python community version has since been handed off to a community fork (&lt;code&gt;endolith/open-interpreter&lt;/code&gt;). The official team rewrote the entire project in Rust, based on OpenAI's Codex codebase, with a sharper focus on a specific question: &lt;strong&gt;how do you get Kimi K3, Qwen, DeepSeek, and other low-cost open models to perform at near-top-tier levels in agent scenarios?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The answer is the Harness system: different models use different agent prompting strategies, letting each model work in the way it does best.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You'll Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The Harness system design: why different models need different agent prompting strategies&lt;/li&gt;
&lt;li&gt;What the Rust rewrite brought: architecture and performance changes&lt;/li&gt;
&lt;li&gt;Computer Use capabilities: browser automation and native UI control&lt;/li&gt;
&lt;li&gt;ACP protocol: what Agent Communication Protocol is&lt;/li&gt;
&lt;li&gt;Codex SDK compatibility layer: one-line model substitution&lt;/li&gt;
&lt;li&gt;Positioning differences vs. Claude Code&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Familiarity with AI coding agent concepts&lt;/li&gt;
&lt;li&gt;Experience using local or low-cost LLMs is helpful&lt;/li&gt;
&lt;li&gt;Understanding how Claude Code, Codex, and similar tools work helps contextualize the Harness concept&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Project Background
&lt;/h2&gt;

&lt;h3&gt;
  
  
  From Python to Rust: A Significant Transformation
&lt;/h3&gt;

&lt;p&gt;Open Interpreter's evolution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2023: Python version launches
  ↓  50k+ Stars within a year
  ↓  Concept validated, but Python has performance and deployment limits

2024-2025: Rust rewrite begins
  ↓  Based on OpenAI Codex codebase
  ↓  Focus: best agent performance for low-cost open models

Late 2025 / 2026: Rust version takes lead
  ↓  Python community version handed to endolith/open-interpreter
  ↓  Official pushes Rust v0.0.26 (July 2026)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This rewrite isn't just a technical upgrade — it's a product repositioning: from "run ChatGPT Code Interpreter locally" to "agent framework optimized for open-weight models."&lt;/p&gt;

&lt;h3&gt;
  
  
  Author / Team
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Organization&lt;/strong&gt;: openinterpreter&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License&lt;/strong&gt;: Apache-2.0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stack&lt;/strong&gt;: Rust 96.6% + Python 2.6% + TypeScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version&lt;/strong&gt;: v0.0.26 (July 2026)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project Stats
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⭐ GitHub Stars: &lt;strong&gt;66,000+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🍴 Forks: 5,700+&lt;/li&gt;
&lt;li&gt;📦 Releases: 57&lt;/li&gt;
&lt;li&gt;📄 License: Apache-2.0&lt;/li&gt;
&lt;li&gt;🦀 Language: Rust 96.6%&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Harness System: Core Innovation
&lt;/h2&gt;

&lt;p&gt;Harness is the most critical design concept in Open Interpreter.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Is a Harness?
&lt;/h3&gt;

&lt;p&gt;Different AI models are trained differently, have different prompt preferences, and use different tool-calling formats. Applying the same agent prompting strategy to every model typically produces poor results with some of them — not because the model is weak, but because the "usage pattern is wrong."&lt;/p&gt;

&lt;p&gt;A harness is "the optimal way to use a specific model as an agent": the system prompt format, tool definition style, context compression strategy, and error recovery logic — all tuned for that particular model.&lt;/p&gt;

&lt;p&gt;Open Interpreter ships multiple harnesses:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Harness&lt;/th&gt;
&lt;th&gt;Target Models&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;native&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Default&lt;/td&gt;
&lt;td&gt;General-purpose&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;claude-code&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Anthropic Claude&lt;/td&gt;
&lt;td&gt;Claude agent format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;claude-code-bare&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Anthropic Claude&lt;/td&gt;
&lt;td&gt;Stripped Claude format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kimi-code&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kimi/Moonshot&lt;/td&gt;
&lt;td&gt;Provider-recommended format, default for Kimi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;qwen-code&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Alibaba Qwen&lt;/td&gt;
&lt;td&gt;Optimized for Qwen series&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deepseek-tui&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DeepSeek&lt;/td&gt;
&lt;td&gt;DeepSeek model format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;swe-agent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;General&lt;/td&gt;
&lt;td&gt;SWE-bench style, software engineering focus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;minimal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;General&lt;/td&gt;
&lt;td&gt;Minimal token consumption&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;zcode&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The Kimi K3 example&lt;/strong&gt;: When you launch with &lt;code&gt;--model kimi&lt;/code&gt;, Open Interpreter automatically uses the &lt;code&gt;kimi-code&lt;/code&gt; harness — the Moonshot AI-recommended, field-validated agent prompting format for Kimi models, implemented natively in Rust. The result is substantially better performance from Kimi K3 on coding tasks compared to using a harness optimized for Claude.&lt;/p&gt;

&lt;h3&gt;
  
  
  Switching Harnesses
&lt;/h3&gt;

&lt;p&gt;Type &lt;code&gt;/harness kimi-code&lt;/code&gt; or &lt;code&gt;/harness qwen-code&lt;/code&gt; directly in the TUI — no restart required.&lt;/p&gt;




&lt;h2&gt;
  
  
  Computer Use
&lt;/h2&gt;

&lt;p&gt;This is the capability that distinguishes Open Interpreter from many coding agents: it doesn't just write code and run commands — it can directly operate graphical interfaces.&lt;/p&gt;

&lt;h3&gt;
  
  
  Browser Automation
&lt;/h3&gt;

&lt;p&gt;Integrates &lt;a href="https://github.com/vercel-labs/agent-browser" rel="noopener noreferrer"&gt;agent-browser&lt;/a&gt; to drive a real browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User: "Find the OpenMontage repo on GitHub, extract the first three paragraphs of the README"
          ↓
Open Interpreter launches browser
          ↓
Navigates to github.com
          ↓
Searches and locates target repository
          ↓
Extracts and returns content
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Native UI Automation
&lt;/h3&gt;

&lt;p&gt;Integrates &lt;a href="https://github.com/trycua/cua" rel="noopener noreferrer"&gt;trycua/cua&lt;/a&gt; for native application control:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;macOS: controls UI elements via Accessibility API&lt;/li&gt;
&lt;li&gt;Windows: uses Windows Automation interface&lt;/li&gt;
&lt;li&gt;Screenshot, click, type — works on any desktop application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;QA Skill&lt;/strong&gt;: A built-in skill built on these two capabilities. The agent automatically tests Web app or desktop app UIs without writing test scripts.&lt;/p&gt;




&lt;h2&gt;
  
  
  ACP Protocol Support
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;ACP (Agent Communication Protocol)&lt;/strong&gt; is an open standard enabling interoperability between different AI agent tools.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run as ACP agent&lt;/span&gt;
interpreter acp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once launched, Open Interpreter acts as a standard ACP agent endpoint, callable from ACP-compatible editors (VS Code, etc.) or other agent orchestration systems. You can use Open Interpreter as the backend agent directly from your IDE, without switching to a separate Open Interpreter interface.&lt;/p&gt;




&lt;h2&gt;
  
  
  Codex SDK Compatibility
&lt;/h2&gt;

&lt;p&gt;If you've built something with OpenAI's Codex SDK, one line of configuration switches the underlying engine to Open Interpreter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"codexPathOverride"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"interpreter"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Existing Codex SDK projects can switch to using local or other open-source models without code changes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Install:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# macOS / Linux&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://www.openinterpreter.com/install | sh

&lt;span class="c"&gt;# Windows&lt;/span&gt;
irm https://www.openinterpreter.com/install.ps1 | iex
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installation, run &lt;code&gt;i&lt;/code&gt; or &lt;code&gt;interpreter&lt;/code&gt; to launch the TUI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configure models:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Use Kimi K3 (automatically enables kimi-code harness)&lt;/span&gt;
interpreter &lt;span class="nt"&gt;--model&lt;/span&gt; kimi

&lt;span class="c"&gt;# Use DeepSeek&lt;/span&gt;
interpreter &lt;span class="nt"&gt;--model&lt;/span&gt; deepseek

&lt;span class="c"&gt;# Use Qwen&lt;/span&gt;
interpreter &lt;span class="nt"&gt;--model&lt;/span&gt; qwen

&lt;span class="c"&gt;# Use Claude&lt;/span&gt;
interpreter &lt;span class="nt"&gt;--model&lt;/span&gt; claude-opus-4-6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;TUI commands:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;/model     Switch model
/harness   Switch harness
/help      All commands
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Config file&lt;/strong&gt; (&lt;code&gt;~/.openinterpreter/config.toml&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[agent]&lt;/span&gt;
&lt;span class="py"&gt;model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"kimi-k3"&lt;/span&gt;
&lt;span class="py"&gt;harness&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"kimi-code"&lt;/span&gt;

&lt;span class="nn"&gt;[permissions]&lt;/span&gt;
&lt;span class="py"&gt;allow_file_writes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;allow_network&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Positioning vs. Claude Code
&lt;/h2&gt;

&lt;p&gt;This question is worth answering directly:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Open Interpreter&lt;/th&gt;
&lt;th&gt;Claude Code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Open source&lt;/td&gt;
&lt;td&gt;✅ Apache-2.0&lt;/td&gt;
&lt;td&gt;Client open source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model support&lt;/td&gt;
&lt;td&gt;Multi-model (Kimi, Qwen, DeepSeek, Claude...)&lt;/td&gt;
&lt;td&gt;Claude only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Depends on model; free/low-cost models available&lt;/td&gt;
&lt;td&gt;Anthropic API pricing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment&lt;/td&gt;
&lt;td&gt;Local, data stays on-device&lt;/td&gt;
&lt;td&gt;Local CLI, model in cloud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Customization&lt;/td&gt;
&lt;td&gt;Replaceable harnesses, extensible components&lt;/td&gt;
&lt;td&gt;Extensions via CLAUDE.md etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Computer Use&lt;/td&gt;
&lt;td&gt;Built-in browser + native UI automation&lt;/td&gt;
&lt;td&gt;Limited bash tool execution&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These aren't in competition — they're different choices. If you primarily use Claude and value a polished experience, Claude Code is the better fit. If you want low-cost models like Kimi K3, fully local operation, or GUI automation capabilities, Open Interpreter is the more appropriate choice.&lt;/p&gt;




&lt;h2&gt;
  
  
  Links and Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🌟 &lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/openinterpreter/openinterpreter" rel="noopener noreferrer"&gt;openinterpreter/openinterpreter&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🌐 &lt;strong&gt;Website&lt;/strong&gt;: &lt;a href="https://www.openinterpreter.com" rel="noopener noreferrer"&gt;openinterpreter.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;Community Python fork&lt;/strong&gt;: &lt;a href="https://github.com/endolith/open-interpreter" rel="noopener noreferrer"&gt;endolith/open-interpreter&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Open Interpreter has gone through a meaningful transformation: from "run ChatGPT Code Interpreter locally" to "Rust agent framework optimized for open-weight models."&lt;/p&gt;

&lt;p&gt;The timing is right. In 2025-2026, Kimi K3, Qwen, DeepSeek, and similar low-cost open models have been rapidly expanding their capability ceiling, increasingly approaching closed top-tier models. But using Claude/GPT-optimized agent formats on these models often produces suboptimal results. The Harness system's value is precise here: each model gets the prompting strategy it does best with.&lt;/p&gt;

&lt;p&gt;The Rust rewrite brings performance advantages. More importantly, it makes ACP protocol support and Codex SDK compatibility possible — transforming Open Interpreter from a standalone tool into infrastructure that integrates into a broader ecosystem.&lt;/p&gt;

&lt;p&gt;66k Stars mostly accumulated in the Python era, but active Rust iteration (57 releases, latest v0.0.26) shows this project continues to be seriously developed.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Explore &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — A marketplace for handpicked AI Agents and skills. Each is validated in real enterprise workflows, stripping away hype and keeping only what truly works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Welcome to my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;Homepage&lt;/a&gt; for more useful insights and interesting products.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>agents</category>
      <category>rust</category>
      <category>claude</category>
    </item>
    <item>
      <title>MCP Series (07): Enterprise Deployment — Security, Authentication, and Version Management</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Thu, 16 Jul 2026 13:46:30 +0000</pubDate>
      <link>https://dev.to/wonderlab/mcp-series-07-enterprise-deployment-security-authentication-and-version-management-2n4n</link>
      <guid>https://dev.to/wonderlab/mcp-series-07-enterprise-deployment-security-authentication-and-version-management-2n4n</guid>
      <description>&lt;h2&gt;
  
  
  Three Gaps Between Local and Production
&lt;/h2&gt;

&lt;p&gt;A local MCP Server takes one command: &lt;code&gt;python server.py&lt;/code&gt;. Enterprise production adds three required problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Authentication&lt;/strong&gt;: stdio mode has no auth — any process can connect. Production needs explicit identity verification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process supervision&lt;/strong&gt;: a Python process that dies doesn't restart itself and generates no alert. Production needs a guardian and health checks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smooth upgrades&lt;/strong&gt;: multiple Agent sessions may connect to the same Server simultaneously. Upgrading can't drop those connections.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Authentication Options
&lt;/h2&gt;

&lt;h3&gt;
  
  
  API Key (preferred for internal services)
&lt;/h3&gt;

&lt;p&gt;Simplest option, suitable for service-to-service calls inside a corporate network:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;mcp.server&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Server&lt;/span&gt;

&lt;span class="n"&gt;EXPECTED_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MCP_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;EXPECTED_API_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MCP_API_KEY environment variable is required&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jira-tools&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;strong&gt;HTTP transport&lt;/strong&gt; (non-stdio), validate in middleware:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;starlette.middleware.base&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseHTTPMiddleware&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;starlette.requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApiKeyMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseHTTPMiddleware&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;call_next&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-API-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
               &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;removeprefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MCP_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unauthorized&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;call_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  OAuth 2.0 (cross-org or user-level permissions)
&lt;/h3&gt;

&lt;p&gt;Use when different users need access to different subsets of data (different Jira projects per user):&lt;/p&gt;

&lt;p&gt;The MCP spec (2025) defines OAuth integration interfaces. The Host (Claude Desktop / Claude Code) acquires the OAuth token during user login and passes it to the Server on each MCP connection.&lt;/p&gt;

&lt;h3&gt;
  
  
  mTLS (high-security internal communication)
&lt;/h3&gt;

&lt;p&gt;For finance, healthcare, or government scenarios with strict data security requirements — mutual certificate verification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;

&lt;span class="n"&gt;ssl_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SSLContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PROTOCOL_TLS_SERVER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ssl_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_cert_chain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/certs/server.crt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/certs/server.key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ssl_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_verify_locations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/certs/ca.crt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ssl_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verify_mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CERT_REQUIRED&lt;/span&gt;  &lt;span class="c1"&gt;# require client cert
&lt;/span&gt;
&lt;span class="c1"&gt;# pass ssl_context to HTTP server
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Authentication selection:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internal service calls (same network)         → API Key + network isolation
Cross-org or user-level permission needs      → OAuth 2.0
High security (finance, healthcare, gov)      → mTLS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Docker Deployment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Minimal Dockerfile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.12-slim&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Separate dependency install (cache layer)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# Don't run as root (security best practice)&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;useradd &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /bin/false mcpuser
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; mcpuser&lt;/span&gt;

&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=30s --timeout=5s --start-period=10s \&lt;/span&gt;
    CMD python -c "import sys; print('healthy')" || exit 1

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "jira_server.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Production Docker Compose
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.prod.yml&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.9"&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;jira-mcp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jira-mcp-server:1.3.0&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;          &lt;span class="c1"&gt;# auto-restart on crash&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MCP_API_KEY=${MCP_API_KEY}&lt;/span&gt;   &lt;span class="c1"&gt;# injected from .env or Secrets&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;JIRA_URL=${JIRA_URL}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;JIRA_TOKEN=${JIRA_TOKEN}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;LOG_LEVEL=INFO&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./logs:/app/logs&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mcp-internal&lt;/span&gt;                 &lt;span class="c1"&gt;# internal only&lt;/span&gt;
    &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;cpus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.5"&lt;/span&gt;
          &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;256M"&lt;/span&gt;
        &lt;span class="na"&gt;reservations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;128M"&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;json-file"&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;max-size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;100m"&lt;/span&gt;
        &lt;span class="na"&gt;max-file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5"&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mcp-internal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridge&lt;/span&gt;
    &lt;span class="na"&gt;internal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;                   &lt;span class="c1"&gt;# no external network access&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key configuration points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;restart: unless-stopped&lt;/code&gt;: restart on crash; stay stopped only on manual stop&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;internal: true&lt;/code&gt;: Docker network with no outbound connection — only containers on the same network can reach the Server&lt;/li&gt;
&lt;li&gt;Resource limits: prevent a buggy Server from consuming host machine resources&lt;/li&gt;
&lt;li&gt;Log rotation: prevent log files from growing unboundedly&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Process Supervision (non-Docker)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# /etc/systemd/system/jira-mcp.service
&lt;/span&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Jira MCP Server&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network.target&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;simple&lt;/span&gt;
&lt;span class="py"&gt;User&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;mcpuser&lt;/span&gt;
&lt;span class="py"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/opt/jira-mcp&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/bin/python3 /opt/jira-mcp/jira_server.py&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
&lt;span class="py"&gt;RestartSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;5&lt;/span&gt;
&lt;span class="py"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"MCP_API_KEY=your-key"&lt;/span&gt;
&lt;span class="py"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"JIRA_TOKEN=your-token"&lt;/span&gt;
&lt;span class="py"&gt;StandardOutput&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;journal&lt;/span&gt;
&lt;span class="py"&gt;StandardError&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;journal&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;jira-mcp
systemctl start jira-mcp
journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; jira-mcp &lt;span class="nt"&gt;-f&lt;/span&gt;    &lt;span class="c"&gt;# live log stream&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Multi-Version Coexistence and Smooth Upgrades
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;Multiple Agent sessions connect to MCP Server v1.2. You need to release v1.3 (adds a new tool) without dropping those connections.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy: Parallel Versions + Traffic Switch
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.prod.yml (parallel versions)&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;jira-mcp-stable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jira-mcp-server:1.2.0&lt;/span&gt;     &lt;span class="c1"&gt;# current stable, serves existing connections&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;mcp-internal&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="na"&gt;jira-mcp-canary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jira-mcp-server:1.3.0&lt;/span&gt;     &lt;span class="c1"&gt;# new version, accepts new connections&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;mcp-internal&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point the Host config at the canary version first. Monitor it. Then switch stable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"jira"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"docker"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"exec"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jira-mcp-canary"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"python"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jira_server.py"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Version Number Rules
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MAJOR.MINOR.PATCH

MAJOR: breaking changes
  → Remove a tool, rename a tool, change required inputSchema fields
  → Agent code that depends on this tool must update in sync

MINOR: backward-compatible additions
  → Add a tool, add optional parameters, extend return fields
  → Existing Agent code continues working; new features opt-in

PATCH: behavior-preserving fixes
  → Bug fixes, performance improvements, logging changes
  → Transparent upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Declare the version in Server code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jira-tools&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.3.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;   &lt;span class="c1"&gt;# returned to Client in initialize response
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deprecation Process
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@server.call_tool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_jira&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="c1"&gt;# old tool name
&lt;/span&gt;        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tool &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;search_jira&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; is deprecated. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Use &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;search_issues&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; instead. Removing in v2.0.0.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;_search_issues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# delegate to new implementation
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the old tool name for 90 days, log usage, then remove it in the MAJOR version.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security Design Checklist
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Authentication and authorization&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Credentials injected via environment variables — never hardcoded in code or image&lt;/li&gt;
&lt;li&gt;[ ] Internal services use API Key; cross-org or user-level permissions use OAuth&lt;/li&gt;
&lt;li&gt;[ ] HTTP transport validates in middleware layer, not in tool handlers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Network isolation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Docker network set to &lt;code&gt;internal: true&lt;/code&gt; — Server has no direct outbound access&lt;/li&gt;
&lt;li&gt;[ ] Only necessary ports exposed (stdio mode requires no open ports)&lt;/li&gt;
&lt;li&gt;[ ] Multiple Servers isolated on separate Docker networks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tool security&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Tool inputs have type validation and range checks (Article 04)&lt;/li&gt;
&lt;li&gt;[ ] High-risk tools (writes, external API calls) have audit logs&lt;/li&gt;
&lt;li&gt;[ ] Server's filesystem access restricted to necessary directories&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Operations&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Logs go to stderr, structured format (JSON), with rotation configured&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;restart: unless-stopped&lt;/code&gt; or systemd supervision ensures availability&lt;/li&gt;
&lt;li&gt;[ ] Resource limits (CPU/memory) prevent abnormal Server behavior from affecting the host&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Match auth to the scenario&lt;/strong&gt;: API Key works for internal services, OAuth for cross-org or user-level access, mTLS for regulated industries — don't over-engineer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker three-pack&lt;/strong&gt;: &lt;code&gt;restart: unless-stopped&lt;/code&gt; (crash recovery) + &lt;code&gt;internal: true&lt;/code&gt; network (isolation) + resource limits (stability)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smooth upgrades need parallel versions&lt;/strong&gt;: MINOR versions are backward-compatible and can replace directly; MAJOR versions run old and new in parallel, letting Agent code migrate at its own pace&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://spec.modelcontextprotocol.io/specification/basic/transports/" rel="noopener noreferrer"&gt;MCP Transport Protocol Specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/develop/security-best-practices/" rel="noopener noreferrer"&gt;Docker Security Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Check out &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — a curated marketplace of AI agents and skills that have been validated in real-world, enterprise-grade workflows. No fluff, just what actually works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Find more useful knowledge and interesting products on my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;Homepage&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>docker</category>
      <category>ai</category>
      <category>llm</category>
    </item>
    <item>
      <title>Open Source Project of the Day (#124): Destructive Command Guard — Intercept rm -rf Before Your AI Agent Runs It</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Wed, 15 Jul 2026 02:16:31 +0000</pubDate>
      <link>https://dev.to/wonderlab/open-source-project-of-the-day-124-destructive-command-guard-intercept-rm-rf-before-your-ai-5365</link>
      <guid>https://dev.to/wonderlab/open-source-project-of-the-day-124-destructive-command-guard-intercept-rm-rf-before-your-ai-5365</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"AI agents occasionally run catastrophic commands — &lt;code&gt;rm -rf ./src&lt;/code&gt;, &lt;code&gt;git reset --hard&lt;/code&gt;, &lt;code&gt;DROP TABLE users&lt;/code&gt; — destroying hours of uncommitted work instantly."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is article &lt;strong&gt;#124&lt;/strong&gt; in the &lt;em&gt;Open Source Project of the Day&lt;/em&gt; series. Today's project is &lt;strong&gt;Destructive Command Guard (dcg)&lt;/strong&gt; — a Rust hook that intercepts dangerous commands before AI coding agents execute them.&lt;/p&gt;

&lt;p&gt;You may have encountered this scenario: you ask Claude Code to clean up temporary files and it uses &lt;code&gt;rm -rf&lt;/code&gt; to delete the entire src directory. Or you ask it to reset a Git state and &lt;code&gt;git reset --hard&lt;/code&gt; wipes two hours of uncommitted work.&lt;/p&gt;

&lt;p&gt;This isn't the AI model becoming dumb. AI agents understand what commands do, but they may underestimate the cost given your specific current state. The gap between "what the command does" and "what it means for this particular moment" is where accidents happen.&lt;/p&gt;

&lt;p&gt;dcg's solution: put a gate before command execution, intercept known dangerous patterns, and return decision authority to the human.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You'll Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;dcg's four-stage processing pipeline: how it makes an interception decision in under a millisecond&lt;/li&gt;
&lt;li&gt;Context awareness — the critical design: why &lt;code&gt;grep "rm -rf"&lt;/code&gt; should not be blocked&lt;/li&gt;
&lt;li&gt;Coverage of 50+ security packs&lt;/li&gt;
&lt;li&gt;Heredoc scanning: handling &lt;code&gt;python -c "os.remove()"&lt;/code&gt; style implicit commands&lt;/li&gt;
&lt;li&gt;Three bypass mechanisms and their design intent&lt;/li&gt;
&lt;li&gt;Claude Code PreToolUse hook integration&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Regular use of Claude Code, Cursor, or similar AI coding tools&lt;/li&gt;
&lt;li&gt;Familiarity with basic shell commands and Git operations&lt;/li&gt;
&lt;li&gt;Direct or secondhand experience with AI agents executing dangerous operations helps contextualize the value&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Project Background
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What Is dcg?
&lt;/h3&gt;

&lt;p&gt;Destructive Command Guard (dcg) is a protection tool that hooks into the command execution layer of AI coding agents. Written in Rust, it integrates as a &lt;code&gt;PreToolUse&lt;/code&gt; hook in Claude Code's execution flow.&lt;/p&gt;

&lt;p&gt;Before a command is executed, it passes through dcg's filter. If the command matches a known dangerous pattern, dcg outputs an interception message and the command doesn't run. If determined safe, the command executes normally with under 1ms latency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Author
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Jeffrey Emanuel&lt;/strong&gt;: Original Python concept, expanded Rust implementation (pack system, Heredoc scanning, context classification)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Darin Gordon&lt;/strong&gt;: Initial Rust port and performance optimizations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License&lt;/strong&gt;: MIT&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project Stats
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⭐ GitHub Stars: &lt;strong&gt;4,400+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🍴 Forks: 165+&lt;/li&gt;
&lt;li&gt;📄 License: MIT&lt;/li&gt;
&lt;li&gt;🦀 Language: Rust (Edition 2024, nightly)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Four-Stage Processing Pipeline
&lt;/h2&gt;

&lt;p&gt;Every command from agent intent to actual execution passes through four stages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI agent intends to execute a command
        ↓
Stage 1: JSON Parsing
        Receive PreToolUse hook JSON payload
        Extract command string
        Invalid payload → fail-open (pass through)
        ↓
Stage 2: Command Normalization
        /usr/bin/git → git
        /opt/homebrew/bin/python3 → python3
        Eliminates false positives from absolute paths
        ↓
Stage 3: Quick Filter
        SIMD-accelerated substring scan
        Skips 99%+ of safe commands instantly
        Suspicious keywords → full analysis
        ↓
Stage 4: Pattern Matching
        Check allowlist first (safe patterns)
        Then check denylist (dangerous patterns)
        Deny → output block message + alternative suggestion
        Allow → command executes normally

Total latency: &amp;lt; 1ms (200ms absolute timeout, then fail-open)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fail-open design principle&lt;/strong&gt;: When dcg itself encounters errors (parse failure, timeout, unexpected panic), it chooses to pass the command through rather than block it. This ensures dcg's own bugs can't freeze your workflow — security protection is an additive layer, not a new failure point.&lt;/p&gt;




&lt;h2&gt;
  
  
  Default-On Protection
&lt;/h2&gt;

&lt;p&gt;No configuration needed after installation. The following protection is immediate:&lt;/p&gt;

&lt;h3&gt;
  
  
  core.filesystem (permanent, cannot be disabled)
&lt;/h3&gt;

&lt;p&gt;Blocks dangerous deletion operations outside temp directories:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Will be blocked:&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ./src
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/Documents
find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.js"&lt;/span&gt; &lt;span class="nt"&gt;-delete&lt;/span&gt;

&lt;span class="c"&gt;# Won't be blocked (legitimate operations):&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /tmp/build_cache
&lt;span class="nb"&gt;rm&lt;/span&gt; /tmp/test_output.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  core.git (default on)
&lt;/h3&gt;

&lt;p&gt;Blocks Git operations that destroy commit history or uncommitted work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Will be blocked:&lt;/span&gt;
git reset &lt;span class="nt"&gt;--hard&lt;/span&gt; HEAD~5       &lt;span class="c"&gt;# destroys uncommitted changes&lt;/span&gt;
git push &lt;span class="nt"&gt;--force&lt;/span&gt;              &lt;span class="c"&gt;# overwrites remote history&lt;/span&gt;
git clean &lt;span class="nt"&gt;-fd&lt;/span&gt;                 &lt;span class="c"&gt;# deletes untracked files&lt;/span&gt;
git rebase &lt;span class="nt"&gt;--root&lt;/span&gt;             &lt;span class="c"&gt;# rewrites entire history&lt;/span&gt;

&lt;span class="c"&gt;# Block message example:&lt;/span&gt;
&lt;span class="c"&gt;# ════════════════════════════════════════════════&lt;/span&gt;
&lt;span class="c"&gt;# BLOCKED  dcg&lt;/span&gt;
&lt;span class="c"&gt;# ────────────────────────────────────────────────&lt;/span&gt;
&lt;span class="c"&gt;# Reason:  git reset --hard destroys uncommitted changes&lt;/span&gt;
&lt;span class="c"&gt;# Command: git reset --hard HEAD~5&lt;/span&gt;
&lt;span class="c"&gt;# Tip:     Consider using 'git stash' first.&lt;/span&gt;
&lt;span class="c"&gt;# ════════════════════════════════════════════════&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  system.disk (default on)
&lt;/h3&gt;

&lt;p&gt;Blocks disk-level dangerous operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Will be blocked:&lt;/span&gt;
mkfs.ext4 /dev/sda
&lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/zero &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/sdb
fdisk /dev/sda
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Context Awareness: The Critical Design
&lt;/h2&gt;

&lt;p&gt;dcg must distinguish between "a command string appearing in data context" and "a command string in execution context":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# These should NOT be blocked:&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"rm -rf"&lt;/span&gt; README.md         &lt;span class="c"&gt;# searching for a string, not executing&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.bashrc | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"reset"&lt;/span&gt;    &lt;span class="c"&gt;# viewing config, not executing&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"never run: rm -rf /"&lt;/span&gt;      &lt;span class="c"&gt;# outputting a string&lt;/span&gt;

&lt;span class="c"&gt;# These SHOULD be blocked:&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ./important_dir
&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /&lt;span class="si"&gt;)&lt;/span&gt;                     &lt;span class="c"&gt;# dangerous command in command substitution&lt;/span&gt;
&lt;span class="sb"&gt;`&lt;/span&gt;git reset &lt;span class="nt"&gt;--hard&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;              &lt;span class="c"&gt;# backtick execution&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The context classification module analyzes the semantic role of the command — is it being passed as an argument to grep/echo/cat, or is it a standalone executable command? That determination drives the decision.&lt;/p&gt;




&lt;h2&gt;
  
  
  Heredoc and Inline Code Scanning
&lt;/h2&gt;

&lt;p&gt;This is a capability most similar tools lack — scanning inline code passed to interpreters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# These attack patterns are scanned:&lt;/span&gt;
python &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import os; os.remove('/etc/passwd')"&lt;/span&gt;
python &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import shutil; shutil.rmtree('./src')"&lt;/span&gt;
bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"rm -rf /var/log/*"&lt;/span&gt;
node &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"require('fs').rmdirSync('./dist', {recursive: true})"&lt;/span&gt;

&lt;span class="c"&gt;# Heredoc form:&lt;/span&gt;
python3 &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
import os
os.remove('/important/file')
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;dcg uses tree-sitter and ast-grep to parse inline code, finding dangerous operations inside it rather than doing naive string matching.&lt;/p&gt;




&lt;h2&gt;
  
  
  50+ Opt-In Security Packs
&lt;/h2&gt;

&lt;p&gt;Beyond default protection, packs can be enabled as needed:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Databases:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[packs]&lt;/span&gt;
&lt;span class="py"&gt;"db.postgres"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;    &lt;span class="c"&gt;# DROP TABLE, TRUNCATE, DELETE without WHERE&lt;/span&gt;
&lt;span class="py"&gt;"db.mysql"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;"db.mongodb"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;"db.redis"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;       &lt;span class="c"&gt;# FLUSHALL, FLUSHDB&lt;/span&gt;
&lt;span class="py"&gt;"db.sqlite"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Kubernetes:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;"k8s.kubectl"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;    &lt;span class="c"&gt;# delete namespace, force delete pods&lt;/span&gt;
&lt;span class="py"&gt;"k8s.helm"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;       &lt;span class="c"&gt;# helm delete, helm rollback&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cloud:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;"cloud.aws"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;      &lt;span class="c"&gt;# s3 rm --recursive, ec2 terminate-instances&lt;/span&gt;
&lt;span class="py"&gt;"cloud.gcp"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;"cloud.azure"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Docker:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;"containers.docker"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  &lt;span class="c"&gt;# docker rm -f, docker system prune -a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Terraform:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;"iac.terraform"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  &lt;span class="c"&gt;# terraform destroy, terraform state rm&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example block with &lt;code&gt;db.redis&lt;/code&gt; enabled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Will be blocked:&lt;/span&gt;
redis-cli FLUSHALL             &lt;span class="c"&gt;# wipes entire Redis instance&lt;/span&gt;
redis-cli FLUSHDB              &lt;span class="c"&gt;# wipes current database&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Three Bypass Mechanisms
&lt;/h2&gt;

&lt;p&gt;dcg is bypassable by design. The goal is "add friction," not "make it impossible":&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Method 1: Single-command bypass (temporary)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;DCG_BYPASS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 git reset &lt;span class="nt"&gt;--hard&lt;/span&gt; HEAD~5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The environment variable only affects this one execution. The same command will still be blocked next time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Method 2: One-time exception&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dcg allow-once &amp;lt;code&amp;gt;   &lt;span class="c"&gt;# code from the authorization code in the block message&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Appropriate when you need to execute one specific operation without affecting future blocking rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Method 3: Permanent allowlist&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dcg allowlist add &lt;span class="s2"&gt;"git reset --hard HEAD"&lt;/span&gt;   &lt;span class="c"&gt;# allow this specific command&lt;/span&gt;
dcg allowlist add &lt;span class="s2"&gt;"rm -rf ./dist"&lt;/span&gt;           &lt;span class="c"&gt;# allow cleaning build directory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Written to config file, permanent.&lt;/p&gt;




&lt;h2&gt;
  
  
  Claude Code Integration
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# One-line install (easy-mode auto-configures Claude Code hook)&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; &lt;span class="s2"&gt;"https://raw.githubusercontent.com/Dicklesworthstone/destructive_command_guard/main/install.sh?&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | bash &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--easy-mode&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This registers a &lt;code&gt;PreToolUse&lt;/code&gt; hook in Claude Code's configuration. From that point, every command Claude Code prepares to execute passes through dcg first.&lt;/p&gt;

&lt;p&gt;Manual configuration (&lt;code&gt;~/.claude/settings.json&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dcg"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  CI Scan Mode
&lt;/h2&gt;

&lt;p&gt;Beyond real-time interception, dcg has a &lt;code&gt;scan&lt;/code&gt; command for auditing dangerous commands already committed to a repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Scan current repository&lt;/span&gt;
dcg scan ./

&lt;span class="c"&gt;# Scan specific file types&lt;/span&gt;
dcg scan ./ &lt;span class="nt"&gt;--include&lt;/span&gt; &lt;span class="s2"&gt;"*.sh,*.yml,Dockerfile,Makefile"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The scanner understands execution context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;RUN rm -rf /var/cache/*&lt;/code&gt; in Dockerfile (reasonable, inside build layer)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;- run: kubectl delete namespace prod&lt;/code&gt; in GitHub Actions workflow (worth flagging)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;clean: rm -rf ./dist&lt;/code&gt; in Makefile (reasonable, clear intent)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rm -rf $TMPDIR&lt;/code&gt; in shell script (check whether TMPDIR might have unexpected value)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Links and Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🌟 &lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Dicklesworthstone/destructive_command_guard" rel="noopener noreferrer"&gt;Dicklesworthstone/destructive_command_guard&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;Install&lt;/strong&gt;: &lt;code&gt;install.sh --easy-mode&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Destructive Command Guard addresses a new risk category that comes with AI coding tool adoption: AI agents have a much broader operational scope than previous automation tools. They actually run shell commands, manipulate file systems, and execute Git operations. A misjudgment now causes much more damage than running a wrong command the old way.&lt;/p&gt;

&lt;p&gt;dcg's design philosophy is "add friction rather than block completely." It doesn't prevent you from doing anything — it just inserts a confirmation step before dangerous operations. Fail-open design ensures it never becomes a failure point itself. Context awareness and Heredoc scanning keep the false positive rate low enough not to interrupt normal work.&lt;/p&gt;

&lt;p&gt;The 50+ security pack coverage extends the protection well beyond the obvious "prevent rm -rf" use case, bringing production-level dangerous operations in databases, Kubernetes, and cloud services into the protection scope.&lt;/p&gt;

&lt;p&gt;For engineers already using Claude Code or similar tools in daily development, the cost to install dcg is minimal. The safety margin it provides is worth having.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Explore &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — A marketplace for handpicked AI Agents and skills. Each is validated in real enterprise workflows, stripping away hype and keeping only what truly works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Welcome to my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;Homepage&lt;/a&gt; for more useful insights and interesting products.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>security</category>
      <category>claude</category>
    </item>
    <item>
      <title>MCP Series (06): MCP vs Function Calling — A Data-Driven Selection Guide</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Tue, 14 Jul 2026 01:57:31 +0000</pubDate>
      <link>https://dev.to/wonderlab/mcp-series-06-mcp-vs-function-calling-a-data-driven-selection-guide-321m</link>
      <guid>https://dev.to/wonderlab/mcp-series-06-mcp-vs-function-calling-a-data-driven-selection-guide-321m</guid>
      <description>&lt;h2&gt;
  
  
  Two Specific Questions
&lt;/h2&gt;

&lt;p&gt;Article 01 covered MCP's architectural advantage: standardized tool reuse. Engineers making an actual selection ask two more concrete questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How large is the MCP process communication overhead? Does it affect user experience?&lt;/li&gt;
&lt;li&gt;When does MCP's code size actually become smaller than Function Calling?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Real benchmark data answers both.&lt;/p&gt;




&lt;h2&gt;
  
  
  Benchmark Design
&lt;/h2&gt;

&lt;p&gt;Test subject: the same "search issues" feature&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Method A (Function Calling):&lt;/strong&gt; tool definition and handler live in Agent code, executed as a direct Python function call&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Method B (MCP):&lt;/strong&gt; tool runs in a standalone Server process, called via stdio subprocess&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;20 calls each (5 warm-up excluded), recording P50/P90/mean.&lt;/p&gt;




&lt;h2&gt;
  
  
  Latency Results
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Method                           Mean       P50       P90       Min
──────────────────────────── ────────  ────────  ────────  ────────
Direct function call            0.01ms     0.01ms     0.01ms    0.005ms
MCP stdio call                  2.09ms     2.05ms     2.37ms     2.00ms

MCP overhead per call: +2.08ms  (283x slower than direct)
MCP server startup (one-time):  570ms
Calls to amortize startup cost: ~274
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;283x sounds alarming. In context: LLM inference takes 5–30 seconds. Tool calls are one step in that chain. At this timescale, 2ms of protocol overhead is imperceptible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When the 2ms overhead actually matters:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Typical Agent task: LLM inference 15s + tool call 2ms → 0.01% overhead
                                                        → negligible

Real-time chatbot: target &amp;lt; 200ms, tool call on critical path
                                                        → evaluate carefully

High-frequency automation: 100 tool calls/second
                           2ms × 100 = 200ms extra latency/sec
                                                        → evaluate carefully
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MCP Server initializes once per session and serves all tool calls after that. Sessions exceeding 274 tool calls (570ms ÷ 2.08ms) reach parity with Function Calling on total latency. Most Agent tasks surpass that threshold; the startup cost amortizes fast.&lt;/p&gt;




&lt;h2&gt;
  
  
  Code Size Results
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Function Calling (definition + handler + Agent loop): 43 lines
MCP Server (standalone process, complete server):     32 lines
MCP Agent code (zero tool code):                       0 lines
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At N=1 the gap is small. It grows with every additional project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Projects sharing one tool    FC total lines    MCP total lines    MCP saves
──────────────────────────────────────────────────────────────────────────
N=1                               43                 32              11
N=2                               86                 32              54
N=3                              129                 32              97
N=5                              215                 32             183
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Function Calling code scales linearly (every Agent maintains its own tool code). MCP code stays constant (Server written once). The reuse benefit compounds with each additional project.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why 283x Is Not the Right Metric for Selection
&lt;/h2&gt;

&lt;p&gt;The raw multiplier misleads. A tool call's actual cost has three components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Total latency = LLM inference + tool execution + protocol overhead

Typical scenario:
  LLM inference:      8,000ms
  Tool execution:       200ms
  MCP protocol:           2ms
  ─────────────────────────────
  Total:              8,202ms

Function Calling: 8,200ms
MCP:              8,202ms

Difference: 0.024%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Protocol overhead disappears next to LLM inference time. Latency doesn't drive the selection decision.&lt;/p&gt;




&lt;h2&gt;
  
  
  Decision Framework
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Core question: how many projects will use this tool?&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Projects = 1, AND:
  Tool logic &amp;lt; 30 lines
  Rapid prototype, unclear if long-term
  → Function Calling

Projects &amp;gt;= 2, OR:
  Tool needs state (connection pool, auth session)
  Non-engineers need to install/configure it (Claude Desktop ecosystem)
  Team wants a shared, centrally maintained tool standard
  → MCP Server

MCP is wrong when:
  Real-time requirement &amp;lt; 10ms (API gateway, live recommendations)
  Tool logic is tightly coupled to Agent logic; separation adds nothing
  Session lifetime is very short (&amp;lt; 100 tool calls) and startup time matters
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Selection logic in one diagram:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tool reuse (N projects × M calls)
    │
    ├── Single project, infrequent ──────→ Function Calling (simpler)
    │
    ├── Multiple projects sharing ────────→ MCP Server (reuse pays off)
    │
    ├── Needs state ───────────────────────→ MCP Server (process holds state)
    │
    └── Real-time requirement &amp;lt; 10ms ────→ Function Calling (no overhead)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Side-by-Side Implementation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Function Calling (43 lines — tool code in every Agent):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Tool schema
&lt;/span&gt;&lt;span class="n"&gt;SEARCH_TOOL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_issues&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Search Jira issues by keyword&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_schema&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;properties&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;required&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Tool implementation
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search_issues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ISSUES&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;summary&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;] &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;summary&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Agent loop (every Agent needs this)
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;anthropic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Anthropic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-sonnet-4-6&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SEARCH_TOOL&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop_reason&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_use&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
        &lt;span class="n"&gt;tc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_use&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search_issues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;([...])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;MCP Server (32 lines once — zero tool code in Agent):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# jira_server.py (written once, reused by every Agent)
&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jira-tools&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@server.list_tools&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list_tools&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_issues&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Search Jira issues by keyword&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;inputSchema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;properties&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
                              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;required&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]})]&lt;/span&gt;

&lt;span class="nd"&gt;@server.call_tool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ISSUES&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;summary&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;] &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;summary&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;TextContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;stdio_server&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nf"&gt;as &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_initialization_options&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="c1"&gt;# Agent configuration: one line in settings.json — no tool code in Agent at all
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Three conclusions from real numbers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;2ms overhead is negligible in Agent contexts&lt;/strong&gt;: LLM inference is 8,000ms, protocol overhead is 2ms — 0.024% of total. "283x slower" is technically accurate but practically misleading&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;570ms startup cost needs a warm-up design&lt;/strong&gt;: if the Agent must respond to the first tool call immediately, initialize the MCP Server at session start rather than on first use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code reuse is the core reason to choose MCP&lt;/strong&gt;: at N=1 the difference is small (43 vs 32 lines); at N=3 projects Function Calling needs 129 lines while MCP stays at 32 — the gap grows with every additional project and every additional tool&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Full demo code: &lt;a href="https://github.com/chendongqi/llm-in-action/tree/main/mcp-06-comparison" rel="noopener noreferrer"&gt;mcp-06-comparison&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Check out &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — a curated marketplace of AI agents and skills that have been validated in real-world, enterprise-grade workflows. No fluff, just what actually works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Find more useful knowledge and interesting products on my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;Homepage&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>performance</category>
      <category>llm</category>
    </item>
    <item>
      <title>Open Source Project of the Day (#123): Vibe-Trading — Quantitative Research in Natural Language, an AI-Powered Personal Trading Agent</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Tue, 14 Jul 2026 01:55:31 +0000</pubDate>
      <link>https://dev.to/wonderlab/open-source-project-of-the-day-123-vibe-trading-quantitative-research-in-natural-language-an-245n</link>
      <guid>https://dev.to/wonderlab/open-source-project-of-the-day-123-vibe-trading-quantitative-research-in-natural-language-an-245n</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"Turn natural language finance questions into runnable analysis, backtests, and trading workflows."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is article &lt;strong&gt;#123&lt;/strong&gt; in the &lt;em&gt;Open Source Project of the Day&lt;/em&gt; series. Today's project is &lt;strong&gt;Vibe-Trading&lt;/strong&gt; — an AI-driven quantitative trading research framework from HKUDS (Hong Kong University Data Science Lab).&lt;/p&gt;

&lt;p&gt;Quantitative research has a high traditional barrier: you need to know which data API to use, how to handle missing data, which backtesting framework to choose, how to write strategy code, how to do statistical validation. Any of these steps can block progress.&lt;/p&gt;

&lt;p&gt;Vibe-Trading's starting premise: &lt;strong&gt;describe in natural language what you want to research, and let the AI agent handle the technical details underneath&lt;/strong&gt;. "Backtest a BTC-USDT 20/50 MA strategy" — one sentence, and the agent automatically selects a data source, fetches data, writes the strategy, runs the backtest, and generates a report.&lt;/p&gt;

&lt;p&gt;This isn't a promise to make you money. It's a research tool, and its own security design makes that clear: read-only defaults, no fund custody, all live trading relays through broker interfaces.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You'll Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Natural language → executable strategy workflow: Plan/Ground/Execute/Validate/Deliver&lt;/li&gt;
&lt;li&gt;19 free data sources and intelligent fallback chain design&lt;/li&gt;
&lt;li&gt;461 pre-built alpha factors (Alpha Zoo)&lt;/li&gt;
&lt;li&gt;Shadow account: analyzing your own trading behavior from historical trade records&lt;/li&gt;
&lt;li&gt;Multi-agent investment committee mode&lt;/li&gt;
&lt;li&gt;Security design: AST sandbox, no fund custody principle&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Basic understanding of quantitative trading concepts (strategy, backtest, alpha factor)&lt;/li&gt;
&lt;li&gt;Python basics&lt;/li&gt;
&lt;li&gt;Trading experience in A-shares, US stocks, or crypto helps contextualize use cases&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Project Background
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What Is Vibe-Trading?
&lt;/h3&gt;

&lt;p&gt;Vibe-Trading is an open-source quantitative research workbench that uses AI agents to translate natural language research intent into executable analysis code and backtesting workflows.&lt;/p&gt;

&lt;p&gt;Its positioning is not "AI that trades for you" but "lowering the technical barrier to quantitative research" — making professional quant tools accessible without requiring software engineering as a prerequisite.&lt;/p&gt;

&lt;h3&gt;
  
  
  Author / Team
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lab&lt;/strong&gt;: HKUDS (Hong Kong University Data Science Lab)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License&lt;/strong&gt;: MIT&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language&lt;/strong&gt;: Python 3.11+&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project Stats
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⭐ GitHub Stars: &lt;strong&gt;21,800+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🍴 Forks: 3,800+&lt;/li&gt;
&lt;li&gt;🛠 Agent tools: 68&lt;/li&gt;
&lt;li&gt;📊 Alpha factors: 461&lt;/li&gt;
&lt;li&gt;📡 Data sources: 19 free + 63+ premium&lt;/li&gt;
&lt;li&gt;📄 License: MIT&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Five-Stage Workflow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Plan
&lt;/h3&gt;

&lt;p&gt;The agent parses the natural language query and decides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which skills to invoke&lt;/li&gt;
&lt;li&gt;Which tools to call&lt;/li&gt;
&lt;li&gt;Which data sources to connect&lt;/li&gt;
&lt;li&gt;Whether to activate multi-agent collaboration&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Ground
&lt;/h3&gt;

&lt;p&gt;Market data fetched via fallback chains:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A-shares (ordered by ascending IP-ban risk):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tencent → mootdx → eastmoney → baostock → akshare → tushare
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;US equities:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yahoo → stooq → sina → yfinance → finnhub → alphavantage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Crypto:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;okx → ccxt → yfinance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One &lt;code&gt;get_market_data&lt;/code&gt; call — upper-layer code doesn't need to know which source is used; the data layer handles fallbacks transparently.&lt;/p&gt;

&lt;h3&gt;
  
  
  Execute
&lt;/h3&gt;

&lt;p&gt;The agent generates strategy code and runs backtest engines inside an AST sandbox. Supported engines:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Market&lt;/th&gt;
&lt;th&gt;Engine&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A-shares&lt;/td&gt;
&lt;td&gt;China A-Share Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;US equities&lt;/td&gt;
&lt;td&gt;US Equity Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HK equities&lt;/td&gt;
&lt;td&gt;HK Equity Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;India&lt;/td&gt;
&lt;td&gt;India NSE/BSE Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Crypto&lt;/td&gt;
&lt;td&gt;Crypto Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;China futures&lt;/td&gt;
&lt;td&gt;China Futures Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Global futures&lt;/td&gt;
&lt;td&gt;Global Futures Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forex&lt;/td&gt;
&lt;td&gt;Forex Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Options&lt;/td&gt;
&lt;td&gt;Options Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mixed portfolio&lt;/td&gt;
&lt;td&gt;CompositeEngine&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Validate
&lt;/h3&gt;

&lt;p&gt;Statistical validation methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monte Carlo simulation&lt;/li&gt;
&lt;li&gt;Bootstrap confidence intervals&lt;/li&gt;
&lt;li&gt;Walk-Forward testing&lt;/li&gt;
&lt;li&gt;Strategy run cards&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Deliver
&lt;/h3&gt;

&lt;p&gt;Output formats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTML / PDF reports&lt;/li&gt;
&lt;li&gt;Pine Script (TradingView)&lt;/li&gt;
&lt;li&gt;TDX (China-specific trading software)&lt;/li&gt;
&lt;li&gt;MetaTrader 5 MQL5&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Core Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Self-Improving Agent
&lt;/h3&gt;

&lt;p&gt;The agent's memory and skill system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cross-session persistent memory&lt;/strong&gt;: Previously researched strategies, discovered patterns, and stated hypotheses are preserved&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;87 skills across 9 categories&lt;/strong&gt;: Quantitative research, factor analysis, risk management, portfolio optimization, and more&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5-layer context compression&lt;/strong&gt;: Handles context expansion in long research sessions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic skill evolution&lt;/strong&gt;: Summarizes new skills during research, expanding the capability library&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Alpha Zoo (Factor Library)
&lt;/h3&gt;

&lt;p&gt;461 pre-built quantitative factors, benchmarkable in one command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Benchmark top 20 factors from GTJA191, CSI300 universe, 2018-2025&lt;/span&gt;
vibe-trading alpha bench &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--zoo&lt;/span&gt; gtja191 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--universe&lt;/span&gt; csi300 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--period&lt;/span&gt; 2018-2025 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--top&lt;/span&gt; 20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output: IC mean, IC standard deviation, IR, and IC-positive ratio per factor.&lt;/p&gt;

&lt;p&gt;Supported factor libraries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Qlib158&lt;/strong&gt;: Classic factor set from Microsoft's open-source quant library&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kakushadze101&lt;/strong&gt;: The classic 101 alpha factors from academic literature&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GTJA191&lt;/strong&gt;: Guotai Junan's 191 quantitative factors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PIT-safe fundamental factors&lt;/strong&gt;: Fundamental data factors designed to avoid Point-in-Time bias&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Shadow Account
&lt;/h3&gt;

&lt;p&gt;One of Vibe-Trading's most interesting features: upload your historical trade records and let AI analyze your behavioral patterns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Upload broker export&lt;/span&gt;
vibe-trading &lt;span class="nt"&gt;--upload&lt;/span&gt; trades_export.csv

&lt;span class="c"&gt;# Analyze trading behavior&lt;/span&gt;
vibe-trading run &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"Analyze my trade history, identify behavioral biases, extract my actual trading strategy"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Diagnosed bias types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Disposition Effect&lt;/strong&gt;: Selling winners too early, holding losers too long&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Overtrading&lt;/strong&gt;: High frequency with no corresponding alpha&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Momentum Chasing&lt;/strong&gt;: Buying after run-ups, selling after drawdowns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anchoring Bias&lt;/strong&gt;: Over-relying on specific price reference points&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quantified bias magnitude report&lt;/li&gt;
&lt;li&gt;Extracted "shadow strategy" (your actual behavioral pattern)&lt;/li&gt;
&lt;li&gt;Comparison against backtest baseline&lt;/li&gt;
&lt;li&gt;HTML/PDF audit report&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Multi-Agent Investment Committees
&lt;/h3&gt;

&lt;p&gt;For complex research tasks, multiple agents can run in parallel:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Composition&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Investment committee&lt;/td&gt;
&lt;td&gt;Analyst, risk officer, portfolio manager in parallel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quant desk&lt;/td&gt;
&lt;td&gt;Factor research, strategy development, backtest validation in division of labor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Crypto team&lt;/td&gt;
&lt;td&gt;On-chain analysis, DEX liquidity, macro hedging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Risk committee&lt;/td&gt;
&lt;td&gt;Multi-dimensional risk assessment and stress testing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Agents work in parallel with streamed progress output, results consolidated into a comprehensive final report.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Install:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;vibe-trading-ai

&lt;span class="c"&gt;# All 16 IM channel adapters (Telegram, Slack, Discord, WeChat, Feishu, etc.)&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"vibe-trading-ai[channels]"&lt;/span&gt;

&lt;span class="c"&gt;# Telegram only&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"vibe-trading-ai[telegram]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Basic usage:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Strategy research and backtest&lt;/span&gt;
vibe-trading run &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"Backtest a BTC-USDT 20/50 MA crossover strategy for 2024"&lt;/span&gt;

&lt;span class="c"&gt;# Macro analysis&lt;/span&gt;
vibe-trading run &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"Analyze current US tech stock valuations compared to 2000 dot-com levels"&lt;/span&gt;

&lt;span class="c"&gt;# Factor research&lt;/span&gt;
vibe-trading run &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"Test momentum factor effectiveness in A-shares, 2015-2025"&lt;/span&gt;

&lt;span class="c"&gt;# Shadow account analysis&lt;/span&gt;
vibe-trading &lt;span class="nt"&gt;--upload&lt;/span&gt; broker_export.csv
vibe-trading run &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"Identify my behavioral biases from the last 2 years of trading"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Python API:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;vibe_trading&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anthropic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-opus-4-6&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Analyze CSI300 monthly momentum effect, backtest 2015-2025, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output annualized return, Sharpe ratio, and maximum drawdown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Docker:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;span class="c"&gt;# WebUI: http://localhost:3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Broker Connections
&lt;/h2&gt;

&lt;p&gt;Live and paper trading via 10 broker integrations:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Market&lt;/th&gt;
&lt;th&gt;Brokers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;US equities&lt;/td&gt;
&lt;td&gt;Robinhood, Alpaca, Interactive Brokers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A-shares / HK&lt;/td&gt;
&lt;td&gt;Futu, Longbridge, Tiger Brokers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Crypto&lt;/td&gt;
&lt;td&gt;OKX, Binance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;India&lt;/td&gt;
&lt;td&gt;Dhan, Shoonya&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Critical safety design:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No fund custody — system only relays trade intent, brokers execute&lt;/li&gt;
&lt;li&gt;Brokers without structural paper/live separation capped at read-only&lt;/li&gt;
&lt;li&gt;Filesystem-level kill switch for emergency stop&lt;/li&gt;
&lt;li&gt;Complete operation audit ledger&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Security Design
&lt;/h2&gt;

&lt;p&gt;Vibe-Trading has done substantial security work — necessary for any system that executes AI-generated code:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AST Sandbox&lt;/strong&gt;: Backtest code runs inside a hardened AST sandbox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Blocked at compile time:
  × network calls (no external network access)
  × subprocess (no spawning subprocesses)
  × eval/exec (no dynamic code execution)
  × os.environ (no environment variable access)
  × file writes outside designated directories
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI-generated strategy code cannot perform any of these dangerous operations regardless of what the code contains.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Additional security measures:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CSRF protection + SSRF defenses&lt;/li&gt;
&lt;li&gt;Rate limiting&lt;/li&gt;
&lt;li&gt;Docker multi-stage builds with digest-pinned images&lt;/li&gt;
&lt;li&gt;Short-lived single-use SSE authentication tickets&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Links and Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🌟 &lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/HKUDS/Vibe-Trading" rel="noopener noreferrer"&gt;HKUDS/Vibe-Trading&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;PyPI&lt;/strong&gt;: &lt;a href="https://pypi.org/project/vibe-trading-ai/" rel="noopener noreferrer"&gt;vibe-trading-ai&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Vibe-Trading represents a direction: lowering the technical barrier to quantitative research from "mastering multiple specialized tools" to "being able to describe what you want to research."&lt;/p&gt;

&lt;p&gt;461 pre-built factors, 19 free data sources, backtest engines for 10 markets — this technical depth isn't a feature list. It's pre-solving the integration problems that anyone doing quantitative research has to work through, so users can focus on the research itself.&lt;/p&gt;

&lt;p&gt;The shadow account feature is rare and genuinely useful. Most retail investors have accumulated trade records they've never systematically analyzed. This feature lets you compare "how I think I trade" with "how I actually trade."&lt;/p&gt;

&lt;p&gt;The security design (AST sandbox, no fund custody, read-only defaults) shows the team understands where this class of system's risks lie and built defenses at the right places.&lt;/p&gt;

&lt;p&gt;21.8k Stars from an academic background team at HKUDS signals both academic credibility and serious engineering.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: This article describes an open-source technical tool and does not constitute investment advice. Historical backtest performance does not guarantee future returns.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Explore &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — A marketplace for handpicked AI Agents and skills. Each is validated in real enterprise workflows, stripping away hype and keeping only what truly works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Welcome to my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;Homepage&lt;/a&gt; for more useful insights and interesting products.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>agents</category>
      <category>ai</category>
      <category>langchain</category>
    </item>
  </channel>
</rss>
