<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>AgentForge Blog</title>
        <link>https://agentforge-tech.github.io/AgentForge/en/blog</link>
        <description>AgentForge Blog</description>
        <lastBuildDate>Sun, 11 Oct 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[Publishing the Open Source Project AgentForge to the Official Maven Central Repository in Practice: Namespace Selection and Automated Publishing]]></title>
            <link>https://agentforge-tech.github.io/AgentForge/en/blog/番外/工程化/开源项目之AgentForge发布到MavenCentral官方仓库实战</link>
            <guid>https://agentforge-tech.github.io/AgentForge/en/blog/番外/工程化/开源项目之AgentForge发布到MavenCentral官方仓库实战</guid>
            <pubDate>Sun, 11 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Publish AgentForge's library modules to Maven Central as the official 1.0.0 release, so that users can import them directly through a Maven dependency.]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>Publish AgentForge's library modules to Maven Central as the official <code>1.0.0</code> release, so that users can import them directly through a Maven dependency.</p>
</blockquote>
<!-- -->
<br>
<p>When working on an open source project, we often run into a very concrete problem: the project runs fine on GitHub, but people who want to use it don't know how to import it. Either they <code>git clone</code> it and run <code>mvn install</code> themselves, or they pass the jar around, which is a terrible experience. <strong>The most elegant approach is to publish it to the official Maven Central repository</strong>, so that users only need to declare one dependency coordinate in <code>pom.xml</code>.</p>
<p>AgentForge's model layer and Agent Runtime were already ready for release, so we decided to formally push them to Maven Central. During the whole process, the first stumbling block was <strong>how to define the groupId namespace</strong>. This article records this complete practice.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-background-and-problem">1. Background and Problem<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#1-background-and-problem" class="hash-link" aria-label="Direct link to 1. Background and Problem" title="Direct link to 1. Background and Problem" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="11-why-publish-to-maven-central">1.1 Why Publish to Maven Central<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#11-why-publish-to-maven-central" class="hash-link" aria-label="Direct link to 1.1 Why Publish to Maven Central" title="Direct link to 1.1 Why Publish to Maven Central" translate="no">​</a></h2>
<ul>
<li class=""><strong>Zero barrier to use</strong>: users only need to declare a dependency coordinate, with no need to clone or compile;</li>
<li class=""><strong>Ecosystem recognition</strong>: it can be indexed by IDEs and dependency platforms (such as mvnrepository), which counts as the "official identity" of an open source project;</li>
<li class=""><strong>Reuse across projects</strong>: combined with a BOM for unified version management, it facilitates future modular evolution.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="12-the-problem-we-encountered">1.2 The Problem We Encountered<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#12-the-problem-we-encountered" class="hash-link" aria-label="Direct link to 1.2 The Problem We Encountered" title="Direct link to 1.2 The Problem We Encountered" translate="no">​</a></h2>
<p>When publishing AgentForge, coordinate naming (groupId) is the first hurdle that cannot be avoided:</p>
<ul>
<li class="">Early on, the project used <code>com.changlu.agentforge</code>;</li>
<li class="">But <strong>Maven Central requires that the groupId must be a namespace you can prove you "own / control"</strong>. Reversing <code>com.changlu</code> gives the domain <code>changlu.com</code>, which we do not own, so verification would be rejected.</li>
</ul>
<p>So the question becomes: <strong>for a personal open source project, how exactly should the groupId be chosen?</strong> This is the core concept that this article will clarify first.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-core-concepts">2. Core Concepts<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#2-core-concepts" class="hash-link" aria-label="Direct link to 2. Core Concepts" title="Direct link to 2. Core Concepts" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-hard-requirements-for-publishing-to-maven-central">2.1 Hard Requirements for Publishing to Maven Central<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#21-hard-requirements-for-publishing-to-maven-central" class="hash-link" aria-label="Direct link to 2.1 Hard Requirements for Publishing to Maven Central" title="Direct link to 2.1 Hard Requirements for Publishing to Maven Central" translate="no">​</a></h2>
<p>Before getting started, you must first know what validations Central performs on uploaded artifacts. It is not a matter of "upload and you're done"; it requires satisfying all of the following at the same time:</p>
<ol>
<li class=""><strong>sources jar + javadoc jar</strong> (required for modules not packaged as <code>pom</code>);</li>
<li class=""><strong>GPG/PGP signature</strong>: each file needs a <code>.asc</code> signature file;</li>
<li class=""><strong>Checksums</strong>: <code>.md5</code> / <code>.sha1</code> are required (<code>.sha256</code> / <code>.sha512</code> are optional), generated automatically by the publishing plugin;</li>
<li class=""><strong>Required POM metadata</strong>: <code>name</code>, <code>description</code>, <code>url</code>, <code>licenses</code>, <code>developers</code>, <code>scm</code>;</li>
<li class=""><strong>The version number must not end with <code>-SNAPSHOT</code></strong>.</li>
</ol>
<blockquote>
<p><strong>Key point</strong>: if any one of the above is not satisfied, the release will fail at the Portal validation stage. All subsequent configuration in this article is built around these 5 items.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-two-sources-of-groupid-namespace-scheme-comparison">2.2 Two Sources of groupId Namespace (Scheme Comparison)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#22-two-sources-of-groupid-namespace-scheme-comparison" class="hash-link" aria-label="Direct link to 2.2 Two Sources of groupId Namespace (Scheme Comparison)" title="Direct link to 2.2 Two Sources of groupId Namespace (Scheme Comparison)" translate="no">​</a></h2>
<p>For personal / small-team projects, there are commonly two paths for a groupId namespace:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="scheme-1-based-on-a-github-account--iogithubusername">Scheme 1: Based on a GitHub Account — <code>io.github.&lt;username&gt;</code><a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#scheme-1-based-on-a-github-account--iogithubusername" class="hash-link" aria-label="Direct link to scheme-1-based-on-a-github-account--iogithubusername" title="Direct link to scheme-1-based-on-a-github-account--iogithubusername" translate="no">​</a></h3>
<ul>
<li class=""><strong>Rule</strong>: <code>io.github.myusername</code>, for example <code>io.github.changluya</code>;</li>
<li class=""><strong>Verification method</strong>: no domain is needed. Sonatype requires you to create a <strong>public repository</strong> with the name specified by the Portal under the <strong>corresponding GitHub account</strong> to prove account ownership; once verified, you can delete it.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="scheme-2-based-on-your-own-domain--reverse-domain">Scheme 2: Based on Your Own Domain — Reverse Domain<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#scheme-2-based-on-your-own-domain--reverse-domain" class="hash-link" aria-label="Direct link to Scheme 2: Based on Your Own Domain — Reverse Domain" title="Direct link to Scheme 2: Based on Your Own Domain — Reverse Domain" translate="no">​</a></h3>
<ul>
<li class=""><strong>Rule</strong>: if you own <code>example.com</code>, use <code>com.example</code>; if you own <code>changlu.cloud</code>, use <code>cloud.changlu</code>, and you can further add a project subspace such as <code>cloud.changlu.agentforge</code>;</li>
<li class=""><strong>Verification method</strong>: add a <strong>TXT record</strong> given by the Portal to <strong>that domain's DNS</strong>.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="comparison-of-the-two-schemes">Comparison of the Two Schemes<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#comparison-of-the-two-schemes" class="hash-link" aria-label="Direct link to Comparison of the Two Schemes" title="Direct link to Comparison of the Two Schemes" translate="no">​</a></h3>
<table><thead><tr><th>Dimension</th><th>Scheme 1: GitHub <code>io.github.&lt;username&gt;</code></th><th>Scheme 2: Reverse of your own domain</th></tr></thead><tbody><tr><td>Verification method</td><td>Create a public GitHub repository</td><td>Add a DNS TXT record</td></tr><tr><td>Cost</td><td>Zero cost</td><td>Requires owning a domain</td></tr><tr><td>Naming freedom</td><td>Locked to the GitHub account name</td><td>Fully autonomous, can use a brand name</td></tr><tr><td>Long-term stability</td><td>Affected if the account is renamed / abandoned</td><td>The domain lives on as long as the name does</td></tr><tr><td>Reuse across projects</td><td>Share one account namespace</td><td>Share one domain namespace</td></tr><tr><td>Applicable scenario</td><td>Fast start without a domain</td><td>Branded projects that already have a domain</td></tr></tbody></table>
<blockquote>
<p><strong>Drawbacks</strong>: although Scheme 1 is zero cost, the name carries the GitHub account name (such as <code>changluya</code>), which is neither attractive nor changeable; once the account is renamed, historical coordinates no longer match the name. Scheme 2 requires you to actually own and be able to operate the domain DNS, but the long-term benefit is greater.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="23-the-choice-of-this-scheme">2.3 The Choice of This Scheme<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#23-the-choice-of-this-scheme" class="hash-link" aria-label="Direct link to 2.3 The Choice of This Scheme" title="Direct link to 2.3 The Choice of This Scheme" translate="no">​</a></h2>
<p><strong>This scheme adopts Scheme 2: the namespace is <code>cloud.changlu</code>, and the project subspace is <code>cloud.changlu.agentforge</code>.</strong></p>
<p>Therefore, <strong>all the preparatory work below (DNS TXT for namespace verification, Token, GPG, settings) is written for the "own domain" path</strong>.</p>
<blockquote>
<p><strong>Note</strong>: if you want to switch to Scheme 1, you only need to change the namespace to <code>io.github.changluya</code> and replace "DNS TXT verification" with "create a public GitHub repository". All other steps (GPG / Token / settings.xml / project modification / publishing process) are <strong>completely identical</strong>.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-overall-scheme-design">3. Overall Scheme Design<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#3-overall-scheme-design" class="hash-link" aria-label="Direct link to 3. Overall Scheme Design" title="Direct link to 3. Overall Scheme Design" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-which-modules-to-publish">3.1 Which Modules to Publish<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#31-which-modules-to-publish" class="hash-link" aria-label="Direct link to 3.1 Which Modules to Publish" title="Direct link to 3.1 Which Modules to Publish" translate="no">​</a></h2>
<p>AgentForge is a multi-module project, and not all modules need to be published externally. We publish the <strong>library modules</strong> and exclude the <strong>example modules</strong>:</p>
<table><thead><tr><th>Module artifactId</th><th>packaging</th><th>Published</th><th>Description</th></tr></thead><tbody><tr><td><code>agentforge-ai-parent</code></td><td>pom</td><td>Yes</td><td>Parent of all modules, must be published</td></tr><tr><td><code>agentforge-ai-bom</code></td><td>pom</td><td>Yes</td><td>BOM, recommended to publish</td></tr><tr><td><code>agentforge-model</code></td><td>pom</td><td>Yes</td><td>Aggregator / parent POM of model submodules</td></tr><tr><td><code>agentforge-model-api</code></td><td>jar</td><td>Yes</td><td></td></tr><tr><td><code>agentforge-model-core</code></td><td>jar</td><td>Yes</td><td></td></tr><tr><td><code>agentforge-model-openai</code></td><td>jar</td><td>Yes</td><td></td></tr><tr><td><code>agentforge-model-anthropic</code></td><td>jar</td><td>Yes</td><td></td></tr><tr><td><code>agentforge-model-registry</code></td><td>jar</td><td>Yes</td><td></td></tr><tr><td><code>agentforge-framework</code></td><td>pom</td><td>Yes</td><td>Aggregator / parent POM of framework submodules</td></tr><tr><td><code>agentforge-agent-core</code></td><td>jar</td><td>Yes</td><td></td></tr><tr><td><code>agentforge-harness-agent</code></td><td>jar</td><td>Yes</td><td></td></tr><tr><td><code>agentforge-aggregator</code> (root)</td><td>pom</td><td>No</td><td>Just an aggregator, users don't need it</td></tr><tr><td><code>agentforge-examples</code> / <code>agentforge-service</code> / <code>-ui</code> / <code>-web</code></td><td>pom/jar</td><td>No</td><td>Examples / applications, not published</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-the-publishing-pipeline">3.2 The Publishing Pipeline<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#32-the-publishing-pipeline" class="hash-link" aria-label="Direct link to 3.2 The Publishing Pipeline" title="Direct link to 3.2 The Publishing Pipeline" translate="no">​</a></h2>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">本地构建</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  → 各模块生成 sources / javadoc jar</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  → 各模块 GPG 签名（.asc）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  → central-publishing-maven-plugin 打包成 bundle</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  → 上传到 Sonatype Central Portal 校验</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  → Portal 发布 → 同步到 Maven Central</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: Sonatype's <code>central-publishing-maven-plugin</code> packages the artifacts of the entire reactor into a single bundle for upload, so we only need to configure it once in the parent, and simply exclude the example modules using <code>excludeArtifacts</code>.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-is-a-script-needed">3.3 Is a Script Needed?<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#33-is-a-script-needed" class="hash-link" aria-label="Direct link to 3.3 Is a Script Needed?" title="Direct link to 3.3 Is a Script Needed?" translate="no">​</a></h2>
<p><strong>Configuring a script is recommended.</strong> Encapsulating "version validation → local signing and packaging dry run → formal deploy" into a <code>release.sh</code> avoids typing long commands by hand and missing parameters every time. The script contents are in Section 7.</p>
<p>Day-to-day development still uses <code>mvn install / test</code>; only releases go through the script.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-prerequisites-account--domain--keys">4. Prerequisites (Account / Domain / Keys)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#4-prerequisites-account--domain--keys" class="hash-link" aria-label="Direct link to 4. Prerequisites (Account / Domain / Keys)" title="Direct link to 4. Prerequisites (Account / Domain / Keys)" translate="no">​</a></h2>
<blockquote>
<p>This section explains the "own domain" path. If you use the GitHub approach, see the explanation in 2.2.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="41-register-for-sonatype-central-portal">4.1 Register for Sonatype Central Portal<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#41-register-for-sonatype-central-portal" class="hash-link" aria-label="Direct link to 4.1 Register for Sonatype Central Portal" title="Direct link to 4.1 Register for Sonatype Central Portal" translate="no">​</a></h2>
<p>Open <a href="https://central.sonatype.com/" target="_blank" rel="noopener noreferrer" class="">https://central.sonatype.com/</a>, register and log in with your email / GitHub, and remember this account — namespaces, Tokens, and publishing are all managed here later.</p>
<p>On first entry, because you logged in with GitHub, the selected namespace is the following <strong>default namespace space</strong>:</p>
<p><img decoding="async" loading="lazy" src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2024/202610042229470.png" alt="img" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="42-add-and-verify-the-namespace-cloudchanglu-own-domain-path">4.2 Add and Verify the Namespace <code>cloud.changlu</code> (Own Domain Path)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#42-add-and-verify-the-namespace-cloudchanglu-own-domain-path" class="hash-link" aria-label="Direct link to 42-add-and-verify-the-namespace-cloudchanglu-own-domain-path" title="Direct link to 42-add-and-verify-the-namespace-cloudchanglu-own-domain-path" translate="no">​</a></h2>
<p><strong>How do you complete namespace verification?</strong> The steps are as follows:</p>
<ol>
<li class="">Log in to the Portal → top-right <strong>Account → Namespaces</strong> (or <strong>Add Namespace</strong> on the home page);</li>
<li class="">Enter <strong><code>cloud.changlu</code></strong> and submit;<!-- -->
<ul>
<li class="">Note: verification targets <strong>the domain itself, <code>changlu.cloud</code></strong>; <code>cloud.changlu</code> is merely its reverse namespace;</li>
</ul>
</li>
</ol>
<p><img decoding="async" loading="lazy" src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2024/202610042229835.png" alt="image-20261004222909676" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2024/202610042229418.png" alt="img" class="img_ev3q"></p>
<ol start="3">
<li class="">
<p>The Portal gives a <strong>Verification Key</strong>; go to the DNS provider of <code>changlu.cloud</code> and add a <strong>TXT record</strong>:</p>
<ul>
<li class="">Host record: <code>@</code> (the root domain <code>changlu.cloud</code>)</li>
<li class="">Record type: <code>TXT</code></li>
<li class="">Record value: the verification value given by the Portal (paste it in full)</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2024/202610042230619.png" alt="img" class="img_ev3q"></p>
</li>
<li class="">
<p><strong>How do you verify whether the TXT has taken effect?</strong> Run locally:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">dig</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-t</span><span class="token plain"> txt changlu.cloud</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># 或</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">host</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-t</span><span class="token plain"> txt changlu.cloud</span><br></div></code></pre></div></div>
<p>Being able to see the TXT value you added is sufficient.</p>
</li>
<li class="">
<p>Return to the Portal and click <strong>Verify</strong>; the status changes to <strong>Verified</strong>.</p>
</li>
</ol>
<p><img decoding="async" loading="lazy" src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2024/202610042230292.png" alt="img" class="img_ev3q"></p>
<blockquote>
<p><strong>Note</strong>: DNS has caching; right after adding it you may need to wait anywhere from a few minutes to tens of minutes; domestic DNS providers may take longer.</p>
</blockquote>
<blockquote>
<p><strong>Key point</strong>: once a namespace is approved, its sub-namespaces (<code>cloud.changlu.agentforge</code>) do not need to be verified again and can be published directly.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="43-install-gpg-generate-a-key-and-upload-the-public-key-including-common-failure-troubleshooting">4.3 Install GPG, Generate a Key, and Upload the Public Key (Including Common Failure Troubleshooting)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#43-install-gpg-generate-a-key-and-upload-the-public-key-including-common-failure-troubleshooting" class="hash-link" aria-label="Direct link to 4.3 Install GPG, Generate a Key, and Upload the Public Key (Including Common Failure Troubleshooting)" title="Direct link to 4.3 Install GPG, Generate a Key, and Upload the Public Key (Including Common Failure Troubleshooting)" translate="no">​</a></h2>
<blockquote>
<p>This section is organized as "install → configure → generate → sign self-check → upload and reverse lookup → backup". Except for the name, public email, and Passphrase in the key wizard, which you must enter manually, the command blocks can all be copied and executed in order. The examples do not contain any real keys, passphrases, or Tokens.</p>
</blockquote>
<p><strong>Step 0: First confirm whether GPG already exists on this machine:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">gpg </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--version</span><br></div></code></pre></div></div>
<ul>
<li class="">If there is output and it is &gt;= 2.2 → skip directly to 4.3.2;</li>
<li class="">If it says <code>command not found</code> → install it as described below.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="431-choose-the-installation-method-by-operating-system">4.3.1 Choose the Installation Method by Operating System<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#431-choose-the-installation-method-by-operating-system" class="hash-link" aria-label="Direct link to 4.3.1 Choose the Installation Method by Operating System" title="Direct link to 4.3.1 Choose the Installation Method by Operating System" translate="no">​</a></h3>
<table><thead><tr><th>Environment</th><th>Recommended path</th><th>Applicability notes</th></tr></thead><tbody><tr><td>macOS (versions currently supported by Homebrew)</td><td>Homebrew bottle</td><td>Shortest path; no local compilation needed when a prebuilt package exists</td></tr><tr><td>Older macOS / Homebrew Tier 3</td><td>MacPorts</td><td>Prefer the binary package for the matching macOS</td></tr><tr><td>macOS (prefer GUI)</td><td>GPG Suite</td><td>Bundles MacGPG, GPG Keychain, and pinentry</td></tr><tr><td>Debian / Ubuntu / WSL</td><td><code>apt</code></td><td>Use the distribution package</td></tr><tr><td>Fedora / RHEL</td><td><code>dnf</code></td><td>Use the distribution package</td></tr><tr><td>Windows 10/11</td><td>Gpg4win</td><td>The officially recommended Windows installer; bundles Kleopatra/pinentry</td></tr></tbody></table>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="macos">macOS<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#macos" class="hash-link" aria-label="Direct link to macOS" title="Direct link to macOS" translate="no">​</a></h4>
<p><strong>Check the macOS version first, then choose the installation path:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">sw_vers </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-productVersion</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">uname</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-m</span><br></div></code></pre></div></div>
<p>Homebrew only continues to produce prebuilt bottles for currently supported macOS versions. Older macOS versions may be classified as Tier 3, in which case <code>brew install gnupg</code> will switch to compiling large dependencies such as OpenSSL from source, which is time-consuming and easily interrupted. Taking the official support matrix as of <strong>2026-10</strong> as an example, on Apple Silicon, macOS 15–27 can use bottles, while macOS 11–14 are Tier 3. Please refer to the real-time description in <a href="https://docs.brew.sh/Support-Tiers" target="_blank" rel="noopener noreferrer" class="">Homebrew Support Tiers</a>.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="path-a-homebrew-currently-supported-macos">Path A: Homebrew (currently supported macOS)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#path-a-homebrew-currently-supported-macos" class="hash-link" aria-label="Direct link to Path A: Homebrew (currently supported macOS)" title="Direct link to Path A: Homebrew (currently supported macOS)" translate="no">​</a></h4>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">brew </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--version</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">brew </span><span class="token function" style="color:rgb(80, 250, 123)">install</span><span class="token plain"> gnupg pinentry-mac</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">command</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-v</span><span class="token plain"> gpg</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">gpg </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--version</span><br></div></code></pre></div></div>
<p>When a matching bottle exists, Homebrew downloads the prebuilt package automatically, with no need to add <code>--force-bottle</code> manually. If it reports <code>has no bottle</code> or spends a long time on <code>build.rb</code> / compiling <code>openssl@3</code>, it means the current system has no matching binary package; do not repeatedly launch <code>brew install</code>, and switch directly to Path B or C.</p>
<blockquote>
<p>On Apple Silicon, Homebrew is under <code>/opt/homebrew</code> by default; if <code>command -v gpg</code> produces no output, run <code>eval "$(/opt/homebrew/bin/brew shellenv)"</code> and write that line into <code>~/.zprofile</code>. On Intel Mac, the default prefix is <code>/usr/local</code>.</p>
</blockquote>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="path-b-macports-recommended-for-older-macos-binary-packages-available">Path B: MacPorts (recommended for older macOS, binary packages available)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#path-b-macports-recommended-for-older-macos-binary-packages-available" class="hash-link" aria-label="Direct link to Path B: MacPorts (recommended for older macOS, binary packages available)" title="Direct link to Path B: MacPorts (recommended for older macOS, binary packages available)" translate="no">​</a></h4>
<ol>
<li class="">Download the <code>.pkg</code> matching your macOS major version (for example Sonoma 14) from the <a href="https://www.macports.org/install.php" target="_blank" rel="noopener noreferrer" class="">MacPorts official installation page</a> and install it;</li>
<li class="">Open a new terminal and run:</li>
</ol>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token builtin class-name" style="color:rgb(189, 147, 249)">cd</span><span class="token plain"> /tmp</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">sudo</span><span class="token plain"> /opt/local/bin/port selfupdate</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># 使用默认 +pinentry（终端口令输入器），不需要完整 Xcode</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">sudo</span><span class="token plain"> /opt/local/bin/port </span><span class="token function" style="color:rgb(80, 250, 123)">install</span><span class="token plain"> gnupg2</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">export</span><span class="token plain"> </span><span class="token assign-left variable environment constant" style="color:rgb(189, 147, 249);font-style:italic">PATH</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"/opt/local/bin:/opt/local/sbin:</span><span class="token string environment constant" style="color:rgb(189, 147, 249)">$PATH</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">command</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-v</span><span class="token plain"> gpg </span><span class="token operator">&amp;&amp;</span><span class="token plain"> gpg </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--version</span><br></div></code></pre></div></div>
<p>MacPorts installs to <code>/opt/local</code> by default. If PATH does not take effect automatically:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'export PATH="/opt/local/bin:/opt/local/sbin:$PATH"'</span><span class="token plain"> </span><span class="token operator">&gt;&gt;</span><span class="token plain"> ~/.zprofile</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">source</span><span class="token plain"> ~/.zprofile</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Do not add <code>+pinentry_mac</code> on a machine that only has Command Line Tools installed</strong>. MacPorts' <code>pinentry-mac</code> port requires a full Xcode, otherwise it reports <code>requires a full Xcode installation</code>. Maven Central signing only needs the default terminal-based <code>pinentry</code>. If you have already started a build with the wrong variant, clean the port's temporary build directory first, then install the default variant:</p>
</blockquote>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token builtin class-name" style="color:rgb(189, 147, 249)">cd</span><span class="token plain"> /tmp</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">sudo</span><span class="token plain"> /opt/local/bin/port clean gnupg2</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">sudo</span><span class="token plain"> /opt/local/bin/port </span><span class="token function" style="color:rgb(80, 250, 123)">install</span><span class="token plain"> gnupg2</span><br></div></code></pre></div></div>
<blockquote>
<p><code>port clean gnupg2</code> only cleans the port's temporary build artifacts; it does not delete project files or uninstall other already-installed dependencies.</p>
</blockquote>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="path-c-gpg-suite-a-fast-gui-option">Path C: GPG Suite (a fast GUI option)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#path-c-gpg-suite-a-fast-gui-option" class="hash-link" aria-label="Direct link to Path C: GPG Suite (a fast GUI option)" title="Direct link to Path C: GPG Suite (a fast GUI option)" translate="no">​</a></h4>
<p>Download an installer compatible with your current macOS from the <a href="https://gpgtools.org/" target="_blank" rel="noopener noreferrer" class="">GPG Suite website</a>. It bundles MacGPG, GPG Keychain, and pinentry, and does not require compiling OpenSSL with Homebrew. GPG Mail is an optional email plugin, and publishing to Maven Central does not need it.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">/usr/local/MacGPG2/bin/gpg </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--version</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'export PATH="/usr/local/MacGPG2/bin:$PATH"'</span><span class="token plain"> </span><span class="token operator">&gt;&gt;</span><span class="token plain"> ~/.zprofile</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">source</span><span class="token plain"> ~/.zprofile</span><br></div></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="linux--wsl">Linux / WSL<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#linux--wsl" class="hash-link" aria-label="Direct link to Linux / WSL" title="Direct link to Linux / WSL" translate="no">​</a></h4>
<p>GnuPG is usually already provided in Linux distributions. For WSL, run the same commands according to its Linux distribution:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># Debian / Ubuntu / WSL Ubuntu</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">sudo</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">apt</span><span class="token plain"> update</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">sudo</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">apt</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">install</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-y</span><span class="token plain"> gnupg2 pinentry-curses</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># Fedora / RHEL</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">sudo</span><span class="token plain"> dnf </span><span class="token function" style="color:rgb(80, 250, 123)">install</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-y</span><span class="token plain"> gnupg2 pinentry</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">gpg </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--version</span><br></div></code></pre></div></div>
<p>For SSH, CI, or pure terminal environments, it is recommended to use <code>pinentry-curses</code> and set it in the current shell:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token builtin class-name" style="color:rgb(189, 147, 249)">export</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">GPG_TTY</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$(</span><span class="token string variable function" style="color:rgb(80, 250, 123);font-style:italic">tty</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">)</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><br></div></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="windows-1011">Windows 10/11<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#windows-1011" class="hash-link" aria-label="Direct link to Windows 10/11" title="Direct link to Windows 10/11" translate="no">​</a></h4>
<p>Download the installer from the <a href="https://www.gpg4win.org/" target="_blank" rel="noopener noreferrer" class="">Gpg4win website</a>. Publishing to Maven Central only needs GnuPG and pinentry; Kleopatra can be used for GUI key management. After installation, reopen PowerShell and verify:</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">gpg --version</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">where.exe gpg</span><br></div></code></pre></div></div>
<p>If <code>gpg</code> is not on PATH, add Gpg4win's <code>bin</code> directory to the user PATH, then reopen PowerShell.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="when-are-command-line-tools-actually-needed">When Are Command Line Tools Actually Needed<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#when-are-command-line-tools-actually-needed" class="hash-link" aria-label="Direct link to When Are Command Line Tools Actually Needed" title="Direct link to When Are Command Line Tools Actually Needed" translate="no">​</a></h4>
<p>If installing gnupg via Homebrew reports the following error:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Error: No developer tools installed.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Install the Command Line Tools:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  xcode-select --install</span><br></div></code></pre></div></div>
<p>It means the system lacks <strong>Command Line Tools</strong> — even if <code>/usr/bin/clang</code> exists, as long as there is no "active developer directory" it will report this. First run:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">xcode-select </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--install</span><br></div></code></pre></div></div>
<p>An installation window pops up; click <strong>Install / 安装</strong> and wait for the download and installation to finish (a few minutes). After completion, confirm:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">xcode-select </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-p</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># 期望输出：/Library/Developer/CommandLineTools</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Note</strong>: when installing a bottle, Apple Silicon usually does not need CLT; it is only required when compiling from source. If you already have full Xcode installed, you can also use <code>sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer</code>.</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="432-verify-path-and-configure-pinentry">4.3.2 Verify PATH and Configure pinentry<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#432-verify-path-and-configure-pinentry" class="hash-link" aria-label="Direct link to 4.3.2 Verify PATH and Configure pinentry" title="Direct link to 4.3.2 Verify PATH and Configure pinentry" translate="no">​</a></h3>
<p>First confirm that Maven can later find the same <code>gpg</code> through PATH:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token builtin class-name" style="color:rgb(189, 147, 249)">command</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-v</span><span class="token plain"> gpg</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">gpg </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--version</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">command</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-v</span><span class="token plain"> pinentry </span><span class="token operator">||</span><span class="token plain"> </span><span class="token boolean">true</span><br></div></code></pre></div></div>
<p><code>command -v gpg</code> must output a real path; then run the corresponding environment command below.</p>
<p>On macOS/Linux it is recommended to write <code>GPG_TTY</code> into the shell config to prevent the passphrase prompt from binding to the wrong terminal:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># zsh（macOS 默认）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">grep</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-qxF</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'export GPG_TTY="$(tty)"'</span><span class="token plain"> ~/.zshrc </span><span class="token operator file-descriptor important">2</span><span class="token operator">&gt;</span><span class="token plain">/dev/null </span><span class="token operator">||</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'export GPG_TTY="$(tty)"'</span><span class="token plain"> </span><span class="token operator">&gt;&gt;</span><span class="token plain"> ~/.zshrc</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">export</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">GPG_TTY</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$(</span><span class="token string variable function" style="color:rgb(80, 250, 123);font-style:italic">tty</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">)</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><br></div></code></pre></div></div>
<p>bash users run:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">grep</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-qxF</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'export GPG_TTY="$(tty)"'</span><span class="token plain"> ~/.bashrc </span><span class="token operator file-descriptor important">2</span><span class="token operator">&gt;</span><span class="token plain">/dev/null </span><span class="token operator">||</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'export GPG_TTY="$(tty)"'</span><span class="token plain"> </span><span class="token operator">&gt;&gt;</span><span class="token plain"> ~/.bashrc</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">export</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">GPG_TTY</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$(</span><span class="token string variable function" style="color:rgb(80, 250, 123);font-style:italic">tty</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">)</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><br></div></code></pre></div></div>
<p>On macOS, the most common errors from <code>gpg --full-generate-key</code> are <code>No pinentry</code> or <code>Inappropriate ioctl for device</code>, caused by not finding the passphrase input program:</p>
<p>Homebrew users can copy directly:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">mkdir</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-p</span><span class="token plain"> ~/.gnupg</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">chmod</span><span class="token plain"> </span><span class="token number">700</span><span class="token plain"> ~/.gnupg</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">PINENTRY</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$(</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">brew </span><span class="token string variable parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--prefix</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">)</span><span class="token string" style="color:rgb(255, 121, 198)">/bin/pinentry-mac"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">touch</span><span class="token plain"> ~/.gnupg/gpg-agent.conf</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">sed</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-i.bak</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'/^pinentry-program /d'</span><span class="token plain"> ~/.gnupg/gpg-agent.conf</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"pinentry-program </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$PINENTRY</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token operator">&gt;&gt;</span><span class="token plain"> ~/.gnupg/gpg-agent.conf</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">chmod</span><span class="token plain"> </span><span class="token number">600</span><span class="token plain"> ~/.gnupg/gpg-agent.conf</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">gpgconf </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--kill</span><span class="token plain"> gpg-agent</span><br></div></code></pre></div></div>
<p>MacPorts users can copy directly:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token builtin class-name" style="color:rgb(189, 147, 249)">export</span><span class="token plain"> </span><span class="token assign-left variable environment constant" style="color:rgb(189, 147, 249);font-style:italic">PATH</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"/opt/local/bin:/opt/local/sbin:</span><span class="token string environment constant" style="color:rgb(189, 147, 249)">$PATH</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">mkdir</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-p</span><span class="token plain"> ~/.gnupg</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">chmod</span><span class="token plain"> </span><span class="token number">700</span><span class="token plain"> ~/.gnupg</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">touch</span><span class="token plain"> ~/.gnupg/gpg-agent.conf</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">sed</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-i.bak</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'/^pinentry-program /d'</span><span class="token plain"> ~/.gnupg/gpg-agent.conf</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'pinentry-program /opt/local/bin/pinentry'</span><span class="token plain"> </span><span class="token operator">&gt;&gt;</span><span class="token plain"> ~/.gnupg/gpg-agent.conf</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">chmod</span><span class="token plain"> </span><span class="token number">600</span><span class="token plain"> ~/.gnupg/gpg-agent.conf</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">gpgconf </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--kill</span><span class="token plain"> gpg-agent</span><br></div></code></pre></div></div>
<p>For Linux pure-terminal users who need to explicitly configure <code>pinentry-curses</code>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">mkdir</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-p</span><span class="token plain"> ~/.gnupg</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">chmod</span><span class="token plain"> </span><span class="token number">700</span><span class="token plain"> ~/.gnupg</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">touch</span><span class="token plain"> ~/.gnupg/gpg-agent.conf</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">sed</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-i.bak</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'/^pinentry-program /d'</span><span class="token plain"> ~/.gnupg/gpg-agent.conf</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"pinentry-program </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$(</span><span class="token string variable builtin class-name" style="color:rgb(189, 147, 249);font-style:italic">command</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> </span><span class="token string variable parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-v</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> pinentry-curses</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">)</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token operator">&gt;&gt;</span><span class="token plain"> ~/.gnupg/gpg-agent.conf</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">chmod</span><span class="token plain"> </span><span class="token number">600</span><span class="token plain"> ~/.gnupg/gpg-agent.conf</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">gpgconf </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--kill</span><span class="token plain"> gpg-agent</span><br></div></code></pre></div></div>
<p>GPG Suite and Gpg4win already bundle and configure pinentry, so you usually do not need to write <code>gpg-agent.conf</code> manually. MacPorts' default <code>/opt/local/bin/pinentry</code> is the terminal version and can be used directly for Maven signing.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="433-generate-a-key-pair">4.3.3 Generate a Key Pair<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#433-generate-a-key-pair" class="hash-link" aria-label="Direct link to 4.3.3 Generate a Key Pair" title="Direct link to 4.3.3 Generate a Key Pair" translate="no">​</a></h3>
<p><strong>On a personal local machine, interactive mode is recommended</strong>, letting pinentry read the Passphrase securely and avoiding writing the passphrase into shell history or a config file:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token builtin class-name" style="color:rgb(189, 147, 249)">export</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">GPG_TTY</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$(</span><span class="token string variable function" style="color:rgb(80, 250, 123);font-style:italic">tty</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">)</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token operator file-descriptor important">2</span><span class="token operator">&gt;</span><span class="token plain">/dev/null </span><span class="token operator">||</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">gpg --full-generate-key</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># RSA and RSA → 4096 → 有效期（例如 2y）→ 姓名/邮箱 → 设置强 Passphrase</span><br></div></code></pre></div></div>
<p>Recommended values:</p>
<ul>
<li class="">Type: <code>RSA and RSA</code>;</li>
<li class="">Length: <code>4096</code>;</li>
<li class="">Validity: <code>2y</code> is recommended; it can be renewed before expiry, which makes risk easier to manage than a permanent key;</li>
<li class="">Name/email: use the publisher identity that will maintain Maven Central artifacts over the long term;</li>
<li class="">Passphrase: use a strong passphrase; do not write it into the repository, scripts, or shell history.</li>
</ul>
<blockquote>
<p>For CI scenarios, you can use <code>--batch</code> to generate a dedicated key, but it is not recommended to use <code>%no-protection</code> as the default option for a personal publishing key. If you must use a passphrase-less CI key, limit its purpose, set an expiry, and store the private key only in a CI Secret.</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="434-view-and-record-the-key_id">4.3.4 View and Record the KEY_ID<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#434-view-and-record-the-key_id" class="hash-link" aria-label="Direct link to 4.3.4 View and Record the KEY_ID" title="Direct link to 4.3.4 View and Record the KEY_ID" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">gpg --list-secret-keys --keyid-format</span><span class="token operator">=</span><span class="token plain">long</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># sec   rsa4096/ABCDEF1234567890 2026-10-04 ...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">#              ^^^^^^^^^^^^^^^^ 即 KEY_ID</span><br></div></code></pre></div></div>
<p>Also record the full 40-character fingerprint; when verifying keys externally you should prefer the fingerprint rather than only comparing the short KEY_ID:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># 自动取当前密钥环中的第一把私钥</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">KEY_ID</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$(</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">gpg </span><span class="token string variable parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--batch</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> --with-colons --list-secret-keys </span><span class="token string variable operator" style="color:rgb(189, 147, 249);font-style:italic">|</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> </span><span class="token string variable function" style="color:rgb(80, 250, 123);font-style:italic">awk</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> -F: </span><span class="token string variable string" style="color:rgb(255, 121, 198);font-style:italic">'$1 == "sec" { print $5; exit }'</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">)</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">FULL_FINGERPRINT</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$(</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">gpg </span><span class="token string variable parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--batch</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> --with-colons </span><span class="token string variable parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--fingerprint</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> </span><span class="token string variable string" style="color:rgb(255, 121, 198);font-style:italic">"</span><span class="token string variable string variable" style="color:rgb(189, 147, 249);font-style:italic">$KEY_ID</span><span class="token string variable string" style="color:rgb(255, 121, 198);font-style:italic">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> </span><span class="token string variable operator" style="color:rgb(189, 147, 249);font-style:italic">|</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> </span><span class="token string variable function" style="color:rgb(80, 250, 123);font-style:italic">awk</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> -F: </span><span class="token string variable string" style="color:rgb(255, 121, 198);font-style:italic">'$1 == "fpr" { print $10; exit }'</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">)</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">test</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-n</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$KEY_ID</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token operator">&amp;&amp;</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">test</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-n</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$FULL_FINGERPRINT</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token operator">||</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'ERROR: 未找到私钥或指纹'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">exit</span><span class="token plain"> </span><span class="token number">1</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"KEY_ID=</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$KEY_ID</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"FULL_FINGERPRINT=</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$FULL_FINGERPRINT</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">gpg </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--fingerprint</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$KEY_ID</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><br></div></code></pre></div></div>
<blockquote>
<p>If there are already multiple private keys in the keyring, first find the key created this time via <code>gpg --list-secret-keys --keyid-format=long</code>, then manually run <code>KEY_ID="16位长KEY_ID"</code>. Subsequent commands are executed in the same terminal by default and reuse these two variables.</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="435-local-signing-and-signature-verification-must-do-before-release">4.3.5 Local Signing and Signature Verification (Must Do Before Release)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#435-local-signing-and-signature-verification-must-do-before-release" class="hash-link" aria-label="Direct link to 4.3.5 Local Signing and Signature Verification (Must Do Before Release)" title="Direct link to 4.3.5 Local Signing and Signature Verification (Must Do Before Release)" translate="no">​</a></h3>
<p>This step simultaneously verifies the private key, pinentry, Passphrase, and GPG agent, making it easier to locate problems than running Maven directly:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># 即使换了新终端，也能自动恢复变量</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">KEY_ID</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${KEY_ID</span><span class="token string variable operator" style="color:rgb(189, 147, 249);font-style:italic">:-</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$(gpg --batch --with-colons --list-secret-keys | awk -F</span><span class="token string variable operator" style="color:rgb(189, 147, 249);font-style:italic">:</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> '$1 == "sec" { print $5; exit }</span><span class="token string" style="color:rgb(255, 121, 198)">')}"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'AgentForge Maven Central GPG signing test'</span><span class="token plain"> </span><span class="token operator">&gt;</span><span class="token plain"> /tmp/agentforge-gpg-test.txt</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">gpg --local-user </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$KEY_ID</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--armor</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  --detach-sign /tmp/agentforge-gpg-test.txt</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">gpg </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--verify</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  /tmp/agentforge-gpg-test.txt.asc </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  /tmp/agentforge-gpg-test.txt</span><br></div></code></pre></div></div>
<p>Seeing <code>Good signature</code> / <code>完好的签名</code> means the local signing pipeline is working. The test file is located in <code>/tmp</code>; do not commit it to the project.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="436-upload-the-public-key-and-reverse-look-it-up-from-the-server">4.3.6 Upload the Public Key and Reverse-Look It Up from the Server<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#436-upload-the-public-key-and-reverse-look-it-up-from-the-server" class="hash-link" aria-label="Direct link to 4.3.6 Upload the Public Key and Reverse-Look It Up from the Server" title="Direct link to 4.3.6 Upload the Public Key and Reverse-Look It Up from the Server" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># 自动恢复 KEY_ID 与完整指纹</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">KEY_ID</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${KEY_ID</span><span class="token string variable operator" style="color:rgb(189, 147, 249);font-style:italic">:-</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$(gpg --batch --with-colons --list-secret-keys | awk -F</span><span class="token string variable operator" style="color:rgb(189, 147, 249);font-style:italic">:</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> '$1 == "sec" { print $5; exit }</span><span class="token string" style="color:rgb(255, 121, 198)">')}"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">FULL_FINGERPRINT</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${FULL_FINGERPRINT</span><span class="token string variable operator" style="color:rgb(189, 147, 249);font-style:italic">:-</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$(gpg --batch --with-colons --fingerprint "$KEY_ID" | awk -F</span><span class="token string variable operator" style="color:rgb(189, 147, 249);font-style:italic">:</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> '$1 == "fpr" { print $10; exit }</span><span class="token string" style="color:rgb(255, 121, 198)">')}"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># 主用：Ubuntu 服务器（较稳定）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">gpg </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--keyserver</span><span class="token plain"> hkps://keyserver.ubuntu.com --send-keys </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$KEY_ID</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># 备用：主用超时时改用它</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">gpg </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--keyserver</span><span class="token plain"> hkps://keys.openpgp.org --send-keys </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$KEY_ID</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># 上传后请使用完整指纹反查，不要只用短 KEY_ID</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-fsSL</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">"https://keyserver.ubuntu.com/pks/lookup?op=get&amp;search=0x</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${FULL_FINGERPRINT}</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token operator">|</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">grep</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'BEGIN PGP PUBLIC KEY BLOCK'</span><br></div></code></pre></div></div>
<p>Outputting <code>BEGIN PGP PUBLIC KEY BLOCK</code> means the keyserver can already return that public key. There may be a brief indexing delay right after upload; just wait a few tens of seconds and check again.</p>
<blockquote>
<p><strong>Note</strong>: <code>keys.openpgp.org</code> <strong>strips user identity</strong>, and requires you to confirm via an email link before the upload actually counts as successful; Maven Central more often pulls from <code>keyserver.ubuntu.com</code>, so <strong>prefer ubuntu</strong>.</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="437-back-up-the-private-key-and-ownertrust">4.3.7 Back Up the Private Key and ownertrust<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#437-back-up-the-private-key-and-ownertrust" class="hash-link" aria-label="Direct link to 4.3.7 Back Up the Private Key and ownertrust" title="Direct link to 4.3.7 Back Up the Private Key and ownertrust" translate="no">​</a></h3>
<p>The public key can be public, but the private key must be stored encrypted. When backing up, first restrict the file permissions of the current shell:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token builtin class-name" style="color:rgb(189, 147, 249)">umask</span><span class="token plain"> 077</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">KEY_ID</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${KEY_ID</span><span class="token string variable operator" style="color:rgb(189, 147, 249);font-style:italic">:-</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$(gpg --batch --with-colons --list-secret-keys | awk -F</span><span class="token string variable operator" style="color:rgb(189, 147, 249);font-style:italic">:</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> '$1 == "sec" { print $5; exit }</span><span class="token string" style="color:rgb(255, 121, 198)">')}"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">GPG_BACKUP_DIR</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string environment constant" style="color:rgb(189, 147, 249)">$HOME</span><span class="token string" style="color:rgb(255, 121, 198)">/agentforge-gpg-backup"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">mkdir</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-p</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$GPG_BACKUP_DIR</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">chmod</span><span class="token plain"> </span><span class="token number">700</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$GPG_BACKUP_DIR</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">gpg </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--armor</span><span class="token plain"> --export-secret-keys </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$KEY_ID</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$GPG_BACKUP_DIR</span><span class="token string" style="color:rgb(255, 121, 198)">/agentforge-private-key.asc"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">gpg --export-ownertrust </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$GPG_BACKUP_DIR</span><span class="token string" style="color:rgb(255, 121, 198)">/agentforge-ownertrust.txt"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">chmod</span><span class="token plain"> </span><span class="token number">600</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$GPG_BACKUP_DIR</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain">/*</span><br></div></code></pre></div></div>
<p><code>$HOME/agentforge-gpg-backup</code> is only a temporary export location. Encrypted disks, offline media, or password managers that support secure attachments are the appropriate long-term backup locations. Delete the temporary directory after the transfer is complete; <strong>do not commit it to Git, do not upload plaintext to cloud storage, and do not send the private key or Passphrase to anyone else</strong>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="438-complete-acceptance-checklist">4.3.8 Complete Acceptance Checklist<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#438-complete-acceptance-checklist" class="hash-link" aria-label="Direct link to 4.3.8 Complete Acceptance Checklist" title="Direct link to 4.3.8 Complete Acceptance Checklist" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token builtin class-name" style="color:rgb(189, 147, 249)">command</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-v</span><span class="token plain"> gpg</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">gpg </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--version</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">gpg --list-secret-keys --keyid-format</span><span class="token operator">=</span><span class="token plain">long</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">KEY_ID</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${KEY_ID</span><span class="token string variable operator" style="color:rgb(189, 147, 249);font-style:italic">:-</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$(gpg --batch --with-colons --list-secret-keys | awk -F</span><span class="token string variable operator" style="color:rgb(189, 147, 249);font-style:italic">:</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> '$1 == "sec" { print $5; exit }</span><span class="token string" style="color:rgb(255, 121, 198)">')}"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">gpg </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--fingerprint</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$KEY_ID</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">gpg </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--verify</span><span class="token plain"> /tmp/agentforge-gpg-test.txt.asc /tmp/agentforge-gpg-test.txt</span><br></div></code></pre></div></div>
<p>Section 4.3 is only considered complete after all of the following conditions are met:</p>
<ul>
<li class=""><code>gpg</code> can be found through PATH;</li>
<li class="">The private key, long KEY_ID, and full fingerprint are visible;</li>
<li class="">The detached signature verifies successfully locally;</li>
<li class="">The Ubuntu keyserver can return the public key by full fingerprint;</li>
<li class="">The private key and ownertrust have been safely backed up outside the repository;</li>
<li class="">The KEY_ID has been recorded for use by the Maven GPG plugin.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="439-common-failures-and-troubleshooting">4.3.9 Common Failures and Troubleshooting<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#439-common-failures-and-troubleshooting" class="hash-link" aria-label="Direct link to 4.3.9 Common Failures and Troubleshooting" title="Direct link to 4.3.9 Common Failures and Troubleshooting" translate="no">​</a></h3>
<table><thead><tr><th>Symptom</th><th>Cause</th><th>Handling</th></tr></thead><tbody><tr><td><code>gpg: command not found</code></td><td>Not installed / install directory not on PATH</td><td>Choose the local path per 4.3.1, and configure <code>/opt/homebrew/bin</code>, <code>/opt/local/bin</code>, or the Gpg4win <code>bin</code></td></tr><tr><td><code>No pinentry</code> / <code>Inappropriate ioctl for device</code></td><td>Missing pinentry / <code>GPG_TTY</code> not set / wrong path configured</td><td>Check pinentry per 4.3.2, set <code>export GPG_TTY="$(tty)"</code>, then <code>gpgconf --kill gpg-agent</code></td></tr><tr><td><code>brew: command not found</code></td><td>Homebrew not installed</td><td>Install per method 1 in 4.3.1</td></tr><tr><td><code>Error: No developer tools installed</code></td><td>Missing Xcode Command Line Tools</td><td>Run <code>xcode-select --install</code>, or switch to method 2 GPG Suite in 4.3.1</td></tr><tr><td>Homebrew spends a long time compiling <code>openssl@3</code></td><td>Current macOS has no matching bottle / Tier 3</td><td>Stop repeated installation and switch to MacPorts' matching macOS <code>.pkg</code> and <code>gnupg2</code></td></tr><tr><td><code>--force-bottle</code> still reports <code>has no bottle</code></td><td>The current formula has not published a bottle for this system</td><td>The parameter cannot conjure a bottle out of thin air; switch to MacPorts / GPG Suite</td></tr><tr><td>MacPorts reports <code>pinentry-mac requires a full Xcode installation</code></td><td><code>+pinentry_mac</code> was explicitly selected</td><td>No need to install full Xcode; run <code>sudo port clean gnupg2 &amp;&amp; sudo port install gnupg2</code> to switch to the default <code>+pinentry</code></td></tr><tr><td>MacPorts reports <code>Requested variants ... do not match</code></td><td>The previous failed build left a variant cache</td><td>Run <code>sudo port clean gnupg2</code>, then reinstall</td></tr><tr><td>Key generation hangs with no response</td><td>The pinentry popup is blocked / TTY is incorrect</td><td>Ensure the terminal is in the foreground, run <code>export GPG_TTY="$(tty)"</code> and <code>gpgconf --kill gpg-agent</code>, then retry</td></tr><tr><td><code>signing failed: No pinentry</code></td><td>Maven process cannot find pinentry at startup</td><td>First verify with the standalone signing test in 4.3.5, and check the absolute path in <code>gpg-agent.conf</code></td></tr><tr><td><code>keyserver send failed: Connection timed out</code></td><td>Public key server network problem</td><td>Switch to <code>hkps://keys.openpgp.org</code> and retry</td></tr><tr><td>The send command stays at <code>sending key</code> for a long time</td><td>The keyserver responds slowly; not necessarily a failure</td><td>First use the HTTPS fingerprint lookup in 4.3.6 for a reverse lookup; if the public key can be found, it has been uploaded</td></tr><tr><td>Not found after uploading to openpgp.org</td><td>Email confirmation required</td><td>Click the confirmation in the email, then re-run <code>--send-keys</code></td></tr><tr><td><code>No secret key</code> during signing</td><td>Wrong KEY_ID / the key is in another keyring</td><td>Verify with <code>gpg --list-secret-keys</code>, or add <code>--local-user "$KEY_ID"</code></td></tr></tbody></table>
<blockquote>
<p><strong>Note</strong>: the private key and Passphrase are only used locally / in CI; <strong>do not commit them to the repository</strong>. Only the public key, KEY_ID, and fingerprint are public.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="44-generate-a-portal-user-token">4.4 Generate a Portal User Token<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#44-generate-a-portal-user-token" class="hash-link" aria-label="Direct link to 4.4 Generate a Portal User Token" title="Direct link to 4.4 Generate a Portal User Token" translate="no">​</a></h2>
<ol>
<li class="">
<p>Log in to <a href="https://central.sonatype.com/" target="_blank" rel="noopener noreferrer" class="">https://central.sonatype.com/</a>;</p>
</li>
<li class="">
<p>Open the user menu at the top right and click <strong>View User Tokens</strong> (or go to <a href="https://central.sonatype.com/account" target="_blank" rel="noopener noreferrer" class="">https://central.sonatype.com/account</a>);</p>
<p><img decoding="async" loading="lazy" src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2024/202610050004619.png" alt="image-20261005000413403" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2024/202610050004722.png" alt="image-20261005000438264" class="img_ev3q"></p>
</li>
<li class="">
<p>Generate a Token on the User Tokens page to get a <strong>username / password</strong> pair (this is a Token, not the login password); it is shown only once, so save it immediately.</p>
</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="45-configure-maven-settingsxml">4.5 Configure Maven <code>settings.xml</code><a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#45-configure-maven-settingsxml" class="hash-link" aria-label="Direct link to 45-configure-maven-settingsxml" title="Direct link to 45-configure-maven-settingsxml" translate="no">​</a></h2>
<p>You can use Maven's default location <code>~/.m2/settings.xml</code>, or place it in a separate directory outside the repository and specify it at release time via <code>mvn -s /absolute/path/settings.xml</code>. The configuration below does not write the GPG Passphrase in plaintext into the XML:</p>
<div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token prolog" style="color:rgb(189, 147, 249)">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">settings</span><span class="token tag" style="color:rgb(255, 121, 198)"> </span><span class="token tag attr-name" style="color:rgb(241, 250, 140)">xmlns</span><span class="token tag attr-value punctuation attr-equals" style="color:rgb(248, 248, 242)">=</span><span class="token tag attr-value punctuation" style="color:rgb(248, 248, 242)">"</span><span class="token tag attr-value" style="color:rgb(255, 121, 198)">http://maven.apache.org/SETTINGS/1.0.0</span><span class="token tag attr-value punctuation" style="color:rgb(248, 248, 242)">"</span><span class="token tag" style="color:rgb(255, 121, 198)"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token tag" style="color:rgb(255, 121, 198)">          </span><span class="token tag attr-name namespace" style="color:rgb(241, 250, 140)">xmlns:</span><span class="token tag attr-name" style="color:rgb(241, 250, 140)">xsi</span><span class="token tag attr-value punctuation attr-equals" style="color:rgb(248, 248, 242)">=</span><span class="token tag attr-value punctuation" style="color:rgb(248, 248, 242)">"</span><span class="token tag attr-value" style="color:rgb(255, 121, 198)">http://www.w3.org/2001/XMLSchema-instance</span><span class="token tag attr-value punctuation" style="color:rgb(248, 248, 242)">"</span><span class="token tag" style="color:rgb(255, 121, 198)"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token tag" style="color:rgb(255, 121, 198)">          </span><span class="token tag attr-name namespace" style="color:rgb(241, 250, 140)">xsi:</span><span class="token tag attr-name" style="color:rgb(241, 250, 140)">schemaLocation</span><span class="token tag attr-value punctuation attr-equals" style="color:rgb(248, 248, 242)">=</span><span class="token tag attr-value punctuation" style="color:rgb(248, 248, 242)">"</span><span class="token tag attr-value" style="color:rgb(255, 121, 198)">http://maven.apache.org/SETTINGS/1.0.0</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token tag attr-value" style="color:rgb(255, 121, 198)">                              https://maven.apache.org/xsd/settings-1.0.0.xsd</span><span class="token tag attr-value punctuation" style="color:rgb(248, 248, 242)">"</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">servers</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">&lt;!-- Central Portal 的 User Token --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">server</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">id</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">central</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">id</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">username</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">YOUR_CENTRAL_TOKEN_USERNAME</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">username</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">password</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">YOUR_CENTRAL_TOKEN_PASSWORD</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">password</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">server</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">servers</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">settings</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><br></div></code></pre></div></div>
<p>Restrict the file permissions:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">chmod</span><span class="token plain"> </span><span class="token number">600</span><span class="token plain"> /absolute/path/settings.xml</span><br></div></code></pre></div></div>
<p>Before releasing, securely read the GPG Passphrase in the current terminal; the input is not echoed and does not enter shell history:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token builtin class-name" style="color:rgb(189, 147, 249)">printf</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'GPG Passphrase: '</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable environment constant" style="color:rgb(189, 147, 249);font-style:italic">IFS</span><span class="token operator">=</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">read</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-r</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-s</span><span class="token plain"> MAVEN_GPG_PASSPHRASE</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">printf</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'\n'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">export</span><span class="token plain"> MAVEN_GPG_PASSPHRASE</span><br></div></code></pre></div></div>
<p>When using a custom configuration file, the Maven command must include <code>-s</code>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">mvn </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-s</span><span class="token plain"> /absolute/path/settings.xml help:effective-settings</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: <code>&lt;server id="central"&gt;</code> corresponds to the publishing plugin's <code>publishingServerId</code>; do not write it as <code>${server}</code>. Maven GPG Plugin 3.2.x reads the passphrase from <code>MAVEN_GPG_PASSPHRASE</code> by default, so there is no longer any need to add a <code>gpg</code> server in <code>settings.xml</code>.</p>
</blockquote>
<blockquote>
<p><code>settings.xml</code> contains the Central Token and must not be committed to Git. This article only uses placeholders and does not show any real Token or Passphrase.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-project-build-modifications-pom">5. Project Build Modifications (POM)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#5-project-build-modifications-pom" class="hash-link" aria-label="Direct link to 5. Project Build Modifications (POM)" title="Direct link to 5. Project Build Modifications (POM)" translate="no">​</a></h2>
<p><strong>Why are only two files changed for the core release configuration?</strong> Because the parent of this repository's modules is <code>agentforge-ai-parent</code>, and the root <code>agentforge-aggregator</code> is not the parent of the modules, therefore:</p>
<ul>
<li class="">Plugins needed by all modules (source/javadoc/gpg/central) → put in <code>agentforge-ai-parent</code>;</li>
<li class="">The root aggregator itself is not published → add <code>maven.deploy.skip=true</code> to the root POM.</li>
</ul>
<p>Central also requires every published POM to have a complete description; if a sub-aggregator module originally lacks a <code>&lt;description&gt;</code>, it must be added in that module's POM. In this project these correspond to <code>agentforge-model/pom.xml</code> and <code>agentforge-framework/pom.xml</code>.</p>
<p>All modifications go into the <strong><code>release</code> profile</strong>, so day-to-day <code>mvn install / test</code> does not trigger them and the behavior is unchanged.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="51-root-pomxml">5.1 Root <code>pom.xml</code><a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#51-root-pomxml" class="hash-link" aria-label="Direct link to 51-root-pomxml" title="Direct link to 51-root-pomxml" translate="no">​</a></h2>
<p>Add to <code>&lt;properties&gt;</code> (only affects the root aggregator itself):</p>
<div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">properties</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">revision</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">1.0.0-SNAPSHOT</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">revision</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)">&lt;!-- 根聚合器不发布到 Central --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">maven.deploy.skip</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">true</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">maven.deploy.skip</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    ...</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">properties</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="52-complete-the-metadata-in-agentforge-ai-parentpomxml">5.2 Complete the Metadata in <code>agentforge-ai-parent/pom.xml</code><a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#52-complete-the-metadata-in-agentforge-ai-parentpomxml" class="hash-link" aria-label="Direct link to 52-complete-the-metadata-in-agentforge-ai-parentpomxml" title="Direct link to 52-complete-the-metadata-in-agentforge-ai-parentpomxml" translate="no">​</a></h2>
<p>Add after <code>&lt;licenses&gt;...&lt;/licenses&gt;</code>:</p>
<div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">developers</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">developer</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">id</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">changlu</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">id</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">name</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">changlu</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">name</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">email</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">YOUR_PUBLIC_EMAIL_OR_NOREPLY_EMAIL</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">email</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">url</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">https://changlu.cloud</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">url</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">organization</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">AgentForge</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">organization</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">organizationUrl</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">https://github.com/changluya/AgentForge</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">organizationUrl</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">developer</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">developers</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">scm</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">connection</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">scm:git:https://github.com/changluya/AgentForge.git</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">connection</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">developerConnection</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">scm:git:ssh://git@github.com/changluya/AgentForge.git</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">developerConnection</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">tag</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">HEAD</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">tag</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">url</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">https://github.com/changluya/AgentForge</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">url</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">scm</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><br></div></code></pre></div></div>
<blockquote>
<p><code>licenses</code> / <code>developers</code> / <code>scm</code> are inherited by submodules and can be read during Central validation; <code>name</code> / <code>description</code> already exist in the current modules. If validation reports a missing <code>url</code>, add <code>&lt;url&gt;</code> in the parent or complete it module by module.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="53-add-a-release-profile-to-agentforge-ai-parentpomxml">5.3 Add a <code>release</code> Profile to <code>agentforge-ai-parent/pom.xml</code><a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#53-add-a-release-profile-to-agentforge-ai-parentpomxml" class="hash-link" aria-label="Direct link to 53-add-a-release-profile-to-agentforge-ai-parentpomxml" title="Direct link to 53-add-a-release-profile-to-agentforge-ai-parentpomxml" translate="no">​</a></h2>
<p>First declare the plugin versions collectively in the existing <code>&lt;properties&gt;</code>:</p>
<div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">gpg.executable</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">gpg</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">gpg.executable</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">&lt;!-- 公开指纹：固定发布密钥，避免多私钥环境误签 --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">gpg.keyname</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">YOUR_FULL_GPG_FINGERPRINT</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">gpg.keyname</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">maven.gpg.plugin.version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">3.2.8</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">maven.gpg.plugin.version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">maven.javadoc.plugin.version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">3.11.2</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">maven.javadoc.plugin.version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">maven.source.plugin.version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">3.3.1</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">maven.source.plugin.version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">central.publishing.maven.plugin.version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">0.11.0</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">central.publishing.maven.plugin.version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><br></div></code></pre></div></div>
<p>Then add before <code>&lt;/project&gt;</code>:</p>
<div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">profiles</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">profile</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">id</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">release</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">id</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">build</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">plugins</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token comment" style="color:rgb(98, 114, 164)">&lt;!-- 1) 为每个待发布 JAR 附加 Central 必需的 sources JAR --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">plugin</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">org.apache.maven.plugins</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">maven-source-plugin</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">${maven.source.plugin.version}</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">executions</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">execution</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                            </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">id</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">attach-sources</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">id</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                            </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">goals</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">goal</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">jar-no-fork</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">goal</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">goals</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">execution</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">executions</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">plugin</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token comment" style="color:rgb(98, 114, 164)">&lt;!-- 2) 附加 Central 必需的 Javadoc JAR；放宽 doclint，避免文档告警阻断发布 --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">plugin</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">org.apache.maven.plugins</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">maven-javadoc-plugin</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">${maven.javadoc.plugin.version}</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">configuration</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">doclint</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">none</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">doclint</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">quiet</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">true</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">quiet</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">configuration</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">executions</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">execution</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                            </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">id</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">attach-javadocs</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">id</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                            </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">goals</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">goal</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">jar</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">goal</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">goals</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">execution</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">executions</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">plugin</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token comment" style="color:rgb(98, 114, 164)">&lt;!-- 3) verify 阶段签名 POM 及全部附件；口令来自 gpg-agent 或 MAVEN_GPG_PASSPHRASE --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">plugin</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">org.apache.maven.plugins</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">maven-gpg-plugin</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">${maven.gpg.plugin.version}</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">configuration</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token comment" style="color:rgb(98, 114, 164)">&lt;!-- 脚本会用 -Dgpg.executable 覆盖为 MacPorts / Homebrew 绝对路径 --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">executable</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">${gpg.executable}</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">executable</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">keyname</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">${gpg.keyname}</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">keyname</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">bestPractices</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">true</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">bestPractices</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">configuration</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">executions</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">execution</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                            </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">id</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">sign-artifacts</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">id</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                            </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">goals</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">goal</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">sign</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">goal</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">goals</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                            </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">phase</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">verify</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">phase</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">execution</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">executions</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">plugin</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token comment" style="color:rgb(98, 114, 164)">&lt;!-- 4) 将 reactor 构件汇总为一个 Central Portal deployment；首发保留手动 Publish --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">plugin</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">org.sonatype.central</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">central-publishing-maven-plugin</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">${central.publishing.maven.plugin.version}</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">extensions</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">true</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">extensions</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">configuration</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">publishingServerId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">central</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">publishingServerId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token comment" style="color:rgb(98, 114, 164)">&lt;!-- 首次建议 false：上传后到 Portal 手工检查再发布 --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">autoPublish</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">false</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">autoPublish</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token comment" style="color:rgb(98, 114, 164)">&lt;!-- 排除示例模块，避免被打进上传 bundle --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">excludeArtifacts</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                            </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">excludeArtifact</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">agentforge-examples</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">excludeArtifact</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                            </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">excludeArtifact</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">agentforge-service</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">excludeArtifact</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                            </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">excludeArtifact</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">agentforge-service-ui</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">excludeArtifact</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                            </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">excludeArtifact</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">agentforge-service-web</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">excludeArtifact</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">excludeArtifacts</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">configuration</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">plugin</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">plugins</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">build</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">profile</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">profiles</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key points</strong>:</p>
<ul>
<li class=""><code>extensions=true</code> lets the plugin take over deploy and upload the entire reactor as <strong>one bundle</strong>;</li>
<li class=""><code>excludeArtifacts</code> excludes by artifactId, ensuring the service applications do not enter the bundle;</li>
<li class=""><code>bestPractices=true</code> forbids passing a plaintext GPG passphrase in the POM / XML; use <code>MAVEN_GPG_PASSPHRASE</code> before releasing;</li>
<li class=""><code>autoPublish=false</code> (first time) requires manually clicking Publish in the Portal after upload; once stable, you can change it to <code>true</code> and add <code>&lt;waitUntil&gt;published&lt;/waitUntil&gt;</code>.</li>
</ul>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="54-version-number-strategy">5.4 Version Number Strategy<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#54-version-number-strategy" class="hash-link" aria-label="Direct link to 5.4 Version Number Strategy" title="Direct link to 5.4 Version Number Strategy" translate="no">​</a></h2>
<ul>
<li class="">
<p>The project uses <code>${revision}</code> to manage the version; <code>flatten-maven-plugin</code> is already configured with <code>resolveCiFriendliesOnly</code>, so <code>${revision}</code> is resolved to the actual version before deployment;</p>
</li>
<li class="">
<p><strong>Do not modify files at release time</strong>; just override on the command line:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-Drevision</span><span class="token operator">=</span><span class="token number">1.0</span><span class="token plain">.0</span><br></div></code></pre></div></div>
</li>
</ul>
<blockquote>
<p><strong>Drawbacks</strong>: an official release <strong>cannot</strong> be <code>1.0.0-SNAPSHOT</code>; and a Central version <strong>cannot be overwritten once published</strong>, so please increment the version number on every release.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="55-local-verification-step-5-no-signing-no-upload">5.5 Local Verification Step 5 (No Signing, No Upload)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#55-local-verification-step-5-no-signing-no-upload" class="hash-link" aria-label="Direct link to 5.5 Local Verification Step 5 (No Signing, No Upload)" title="Direct link to 5.5 Local Verification Step 5 (No Signing, No Upload)" translate="no">​</a></h2>
<p>Run in the repository root directory. Here we select only the 11 modules that will actually be published, to avoid the service application project downloading a large number of irrelevant dependencies:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">SETTINGS_FILE</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"/absolute/path/settings.xml"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">RELEASE_MODULES</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">'agentforge-ai-parent,agentforge-ai-bom,agentforge-model,agentforge-model/agentforge-model-api,agentforge-model/agentforge-model-core,agentforge-model/agentforge-model-openai,agentforge-model/agentforge-model-anthropic,agentforge-model/agentforge-model-registry,agentforge-framework,agentforge-framework/agentforge-agent-core,agentforge-framework/agentforge-harness-agent'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">mvn </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-s</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$SETTINGS_FILE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-P</span><span class="token plain"> release </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-Drevision</span><span class="token operator">=</span><span class="token number">1.0</span><span class="token plain">.0 </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-DskipTests</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-Dgpg.skip</span><span class="token operator">=</span><span class="token plain">true </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-pl</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$RELEASE_MODULES</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  verify</span><br></div></code></pre></div></div>
<p>Expected result: the log shows <code>Inspecting build with total of 10 modules</code>, <code>Installing Central Publishing features</code>, and finally <code>BUILD SUCCESS</code>. In the <code>target/</code> of each JAR module there should also be:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">&lt;artifactId&gt;-1.0.0.jar</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">&lt;artifactId&gt;-1.0.0-sources.jar</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">&lt;artifactId&gt;-1.0.0-javadoc.jar</span><br></div></code></pre></div></div>
<blockquote>
<p><code>-Dgpg.skip=true</code> is only used for the POM / artifact structure verification in step 5; step 6's dry run and the formal release must remove it to ensure Central receives the <code>.asc</code> signatures.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-real-world-publishing-and-one-click-deployment">6. Real-World Publishing and One-Click Deployment<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#6-real-world-publishing-and-one-click-deployment" class="hash-link" aria-label="Direct link to 6. Real-World Publishing and One-Click Deployment" title="Direct link to 6. Real-World Publishing and One-Click Deployment" translate="no">​</a></h2>
<p>This section is the linear runbook compiled after AgentForge <code>1.0.0</code> was actually run through successfully. Subsequent release personnel do not need to piece together Maven parameters by hand; <strong>start from 6.1 and execute in order, and do not proceed to the next step if any step fails</strong>.</p>
<p>The one-click script is located at <code>bin/local/release.sh</code>, with documentation at <code>bin/local/README.md</code>. The script automatically limits to the 11 public library modules, excludes examples / service, probes the absolute path of GPG, and performs an independent signing precheck before Maven.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="61-step-0-create-the-one-click-release-script">6.1 Step 0: Create the One-Click Release Script<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#61-step-0-create-the-one-click-release-script" class="hash-link" aria-label="Direct link to 6.1 Step 0: Create the One-Click Release Script" title="Direct link to 6.1 Step 0: Create the One-Click Release Script" translate="no">​</a></h2>
<p>Create <code>bin/local/release.sh</code> in the repository root; the complete contents are as follows:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token shebang important">#!/usr/bin/env bash</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">set</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-euo</span><span class="token plain"> pipefail</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">readonly</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">RELEASE_MODULES</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">'agentforge-ai-parent,agentforge-ai-bom,agentforge-model,agentforge-model/agentforge-model-api,agentforge-model/agentforge-model-core,agentforge-model/agentforge-model-openai,agentforge-model/agentforge-model-anthropic,agentforge-model/agentforge-model-registry,agentforge-framework,agentforge-framework/agentforge-agent-core,agentforge-framework/agentforge-harness-agent'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function-name function" style="color:rgb(80, 250, 123)">usage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token function" style="color:rgb(80, 250, 123)">cat</span><span class="token plain"> </span><span class="token operator">&lt;&lt;</span><span class="token string" style="color:rgb(255, 121, 198)">'EOF'</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">Usage: bin/local/release.sh &lt;version&gt; [options]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">Options:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">  --settings &lt;file&gt;  Maven settings.xml (default: $MAVEN_SETTINGS or ~/.m2/settings.xml)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">  --dry-run          Build and sign the release artifacts without uploading</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">  --auto-publish     Upload and publish automatically instead of waiting in Portal</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">  --skip-tests       Skip tests (not recommended for a formal release)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">  -h, --help         Show this help</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">EOF</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> </span><span class="token variable" style="color:rgb(189, 147, 249);font-style:italic">$#</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-gt</span><span class="token plain"> </span><span class="token number">0</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">||</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> usage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">exit</span><span class="token plain"> </span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$1</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token operator">==</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'-h'</span><span class="token plain"> </span><span class="token operator">||</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$1</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token operator">==</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'--help'</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">then</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    usage</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">exit</span><span class="token plain"> </span><span class="token number">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">fi</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">VERSION</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$1</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">shift</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">SETTINGS_FILE</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${MAVEN_SETTINGS</span><span class="token string variable operator" style="color:rgb(189, 147, 249);font-style:italic">:-</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${</span><span class="token string variable environment constant" style="color:rgb(189, 147, 249);font-style:italic">HOME</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">}</span><span class="token string" style="color:rgb(255, 121, 198)">/.m2/settings.xml}"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">DRY_RUN</span><span class="token operator">=</span><span class="token number">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">AUTO_PUBLISH</span><span class="token operator">=</span><span class="token number">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">SKIP_TESTS</span><span class="token operator">=</span><span class="token number">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">while</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> </span><span class="token variable" style="color:rgb(189, 147, 249);font-style:italic">$#</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-gt</span><span class="token plain"> </span><span class="token number">0</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">do</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">case</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$1</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">in</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        --settings</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> </span><span class="token variable" style="color:rgb(189, 147, 249);font-style:italic">$#</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-ge</span><span class="token plain"> </span><span class="token number">2</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">||</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'Error: --settings requires a file path.'</span><span class="token plain"> </span><span class="token operator">&gt;</span><span class="token file-descriptor important">&amp;2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">exit</span><span class="token plain"> </span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">SETTINGS_FILE</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$2</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">shift</span><span class="token plain"> </span><span class="token number">2</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        --dry-run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">DRY_RUN</span><span class="token operator">=</span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">shift</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        --auto-publish</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">AUTO_PUBLISH</span><span class="token operator">=</span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">shift</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        --skip-tests</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">SKIP_TESTS</span><span class="token operator">=</span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">shift</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        -h</span><span class="token operator">|</span><span class="token plain">--help</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> usage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">exit</span><span class="token plain"> </span><span class="token number">0</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        *</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Error: unknown option: </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$1</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token operator">&gt;</span><span class="token file-descriptor important">&amp;2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> usage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">exit</span><span class="token plain"> </span><span class="token number">1</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">esac</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">done</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$VERSION</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token operator">=~</span><span class="token plain"> ^</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token number">0</span><span class="token plain">-9</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain">+</span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token number">0</span><span class="token plain">-9</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain">+</span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token number">0</span><span class="token plain">-9</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain">+</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">.-</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">A-Za-z0-9</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain">+</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">*$ </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">||</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Error: invalid release version: </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$VERSION</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token operator">&gt;</span><span class="token file-descriptor important">&amp;2</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">exit</span><span class="token plain"> </span><span class="token number">1</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$VERSION</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token operator">!=</span><span class="token plain"> *-SNAPSHOT </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">||</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'Error: a Central release cannot use -SNAPSHOT.'</span><span class="token plain"> </span><span class="token operator">&gt;</span><span class="token file-descriptor important">&amp;2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">exit</span><span class="token plain"> </span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-f</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$SETTINGS_FILE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">||</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Error: settings.xml not found: </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$SETTINGS_FILE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token operator">&gt;</span><span class="token file-descriptor important">&amp;2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">exit</span><span class="token plain"> </span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">command</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-v</span><span class="token plain"> mvn </span><span class="token operator">&gt;</span><span class="token plain">/dev/null </span><span class="token operator">||</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'Error: mvn was not found in PATH.'</span><span class="token plain"> </span><span class="token operator">&gt;</span><span class="token file-descriptor important">&amp;2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">exit</span><span class="token plain"> </span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># GUI Terminal 的 PATH 可能没有 MacPorts / Homebrew，因此显式探测 GPG。</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">GPG_EXECUTABLE</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${GPG_EXECUTABLE</span><span class="token string variable operator" style="color:rgb(189, 147, 249);font-style:italic">:-</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">}</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-z</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$GPG_EXECUTABLE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">then</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">for</span><span class="token plain"> </span><span class="token for-or-select variable" style="color:rgb(189, 147, 249);font-style:italic">candidate</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">in</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$(</span><span class="token string variable builtin class-name" style="color:rgb(189, 147, 249);font-style:italic">command</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> </span><span class="token string variable parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-v</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> gpg </span><span class="token string variable operator file-descriptor important" style="color:rgb(189, 147, 249);font-style:italic">2</span><span class="token string variable operator" style="color:rgb(189, 147, 249);font-style:italic">&gt;</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">/dev/null </span><span class="token string variable operator" style="color:rgb(189, 147, 249);font-style:italic">||</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> </span><span class="token string variable boolean" style="color:rgb(189, 147, 249);font-style:italic">true</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">)</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> /opt/local/bin/gpg /opt/homebrew/bin/gpg /usr/local/bin/gpg</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">do</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-n</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$candidate</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token operator">&amp;&amp;</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-x</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$candidate</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">then</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">GPG_EXECUTABLE</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$candidate</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">break</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">fi</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">done</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">fi</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-n</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$GPG_EXECUTABLE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">||</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'Error: gpg was not found.'</span><span class="token plain"> </span><span class="token operator">&gt;</span><span class="token file-descriptor important">&amp;2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">exit</span><span class="token plain"> </span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">export</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">GPG_TTY</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${GPG_TTY</span><span class="token string variable operator" style="color:rgb(189, 147, 249);font-style:italic">:-</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$(tty)}</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># 口令隐藏输入，不放入命令行、POM 或 settings.xml。</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-z</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${MAVEN_GPG_PASSPHRASE</span><span class="token string variable operator" style="color:rgb(189, 147, 249);font-style:italic">:-</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">}</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">then</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">printf</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'GPG Passphrase (input hidden): '</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token assign-left variable environment constant" style="color:rgb(189, 147, 249);font-style:italic">IFS</span><span class="token operator">=</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">read</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-r</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-s</span><span class="token plain"> MAVEN_GPG_PASSPHRASE</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">printf</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'\n'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-n</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$MAVEN_GPG_PASSPHRASE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">||</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'Error: GPG Passphrase cannot be empty.'</span><span class="token plain"> </span><span class="token operator">&gt;</span><span class="token file-descriptor important">&amp;2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">exit</span><span class="token plain"> </span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">export</span><span class="token plain"> MAVEN_GPG_PASSPHRASE</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">fi</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># Maven 前先对临时文件签名和验签，尽早发现错误口令或 GPG 配置。</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">SIGN_CHECK_FILE</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$(</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">mktemp /tmp/agentforge-gpg-check.XXXXXX</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">)</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function-name function" style="color:rgb(80, 250, 123)">cleanup_sign_check</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token function" style="color:rgb(80, 250, 123)">rm</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-f</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$SIGN_CHECK_FILE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${SIGN_CHECK_FILE}</span><span class="token string" style="color:rgb(255, 121, 198)">.asc"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">trap</span><span class="token plain"> cleanup_sign_check EXIT</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">printf</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'AgentForge Maven Central signing check\n'</span><span class="token plain"> </span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$SIGN_CHECK_FILE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token operator">!</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">printf</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'%s\n'</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$MAVEN_GPG_PASSPHRASE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token operator">|</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$GPG_EXECUTABLE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--batch</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--yes</span><span class="token plain"> --no-tty --pinentry-mode loopback --passphrase-fd </span><span class="token number">0</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--armor</span><span class="token plain"> --detach-sign </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$SIGN_CHECK_FILE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">then</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'Error: GPG preflight signing failed. Enter the private-key Passphrase created with this GPG key.'</span><span class="token plain"> </span><span class="token operator">&gt;</span><span class="token file-descriptor important">&amp;2</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'       Do not enter the Central Token, KEY_ID, fingerprint, or macOS login password.'</span><span class="token plain"> </span><span class="token operator">&gt;</span><span class="token file-descriptor important">&amp;2</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">exit</span><span class="token plain"> </span><span class="token number">1</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">fi</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$GPG_EXECUTABLE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--batch</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--verify</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${SIGN_CHECK_FILE}</span><span class="token string" style="color:rgb(255, 121, 198)">.asc"</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$SIGN_CHECK_FILE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">cleanup_sign_check</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">trap</span><span class="token plain"> - EXIT</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">MVN_ARGS</span><span class="token operator">=</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-s</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$SETTINGS_FILE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-P</span><span class="token plain"> release</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token string" style="color:rgb(255, 121, 198)">"-Drevision=</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${VERSION}</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token string" style="color:rgb(255, 121, 198)">"-Dgpg.executable=</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${GPG_EXECUTABLE}</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-pl</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$RELEASE_MODULES</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$SKIP_TESTS</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-eq</span><span class="token plain"> </span><span class="token number">1</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">&amp;&amp;</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">MVN_ARGS</span><span class="token operator">+=</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">-DskipTests</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"&gt;&gt; version=</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$VERSION</span><span class="token string" style="color:rgb(255, 121, 198)"> dryRun=</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$DRY_RUN</span><span class="token string" style="color:rgb(255, 121, 198)"> autoPublish=</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$AUTO_PUBLISH</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"&gt;&gt; settings=</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$SETTINGS_FILE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"&gt;&gt; gpg=</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$GPG_EXECUTABLE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$DRY_RUN</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-eq</span><span class="token plain"> </span><span class="token number">1</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">then</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    mvn </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${MVN_ARGS</span><span class="token string variable punctuation" style="color:rgb(248, 248, 242);font-style:italic">[</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">@</span><span class="token string variable punctuation" style="color:rgb(248, 248, 242);font-style:italic">]</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">}</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> clean verify</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'&gt;&gt; Dry run succeeded: artifacts were built and signed; nothing was uploaded.'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">exit</span><span class="token plain"> </span><span class="token number">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">fi</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$AUTO_PUBLISH</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-eq</span><span class="token plain"> </span><span class="token number">1</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">then</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">MVN_ARGS</span><span class="token operator">+=</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">-Dcentral.publishing.autoPublish</span><span class="token operator">=</span><span class="token plain">true </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-Dcentral.publishing.waitUntil</span><span class="token operator">=</span><span class="token plain">published</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">fi</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">mvn </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${MVN_ARGS</span><span class="token string variable punctuation" style="color:rgb(248, 248, 242);font-style:italic">[</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">@</span><span class="token string variable punctuation" style="color:rgb(248, 248, 242);font-style:italic">]</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">}</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> clean deploy</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$AUTO_PUBLISH</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-eq</span><span class="token plain"> </span><span class="token number">1</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">then</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'&gt;&gt; Central deployment was uploaded and automatic publishing was requested.'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">else</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'&gt;&gt; Central deployment was uploaded. Review and publish it in the Portal:'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'   https://central.sonatype.com/publishing/deployments'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">fi</span><br></div></code></pre></div></div>
<p>Do not fill in any Token, Passphrase, or private key contents in the script.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="62-step-1-enter-the-repository-and-set-the-release-parameters">6.2 Step 1: Enter the Repository and Set the Release Parameters<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#62-step-1-enter-the-repository-and-set-the-release-parameters" class="hash-link" aria-label="Direct link to 6.2 Step 1: Enter the Repository and Set the Release Parameters" title="Direct link to 6.2 Step 1: Enter the Repository and Set the Release Parameters" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token builtin class-name" style="color:rgb(189, 147, 249)">cd</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/absolute/path/AgentForge"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">VERSION</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"1.0.0"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">SETTINGS_FILE</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"/absolute/path/settings.xml"</span><br></div></code></pre></div></div>
<p><code>VERSION</code> must be a non-SNAPSHOT version that has never been published to Central before. <code>SETTINGS_FILE</code> must be located outside the Git repository.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="63-step-2-pre-release-self-check">6.3 Step 2: Pre-Release Self-Check<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#63-step-2-pre-release-self-check" class="hash-link" aria-label="Direct link to 6.3 Step 2: Pre-Release Self-Check" title="Direct link to 6.3 Step 2: Pre-Release Self-Check" translate="no">​</a></h2>
<p>First run the following in order:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">chmod</span><span class="token plain"> +x bin/local/release.sh</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">chmod</span><span class="token plain"> </span><span class="token number">600</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$SETTINGS_FILE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># 脚本语法与用法</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">bash</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-n</span><span class="token plain"> bin/local/release.sh</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">bin/local/release.sh </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--help</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># settings.xml 必须存在；macOS 可额外用 xmllint 检查 XML</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">test</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-f</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$SETTINGS_FILE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">xmllint </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--noout</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$SETTINGS_FILE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># 必须能看到至少一把 sec 私钥</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">GPG_BIN</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$(</span><span class="token string variable builtin class-name" style="color:rgb(189, 147, 249);font-style:italic">command</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> </span><span class="token string variable parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-v</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> gpg </span><span class="token string variable operator" style="color:rgb(189, 147, 249);font-style:italic">||</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> </span><span class="token string variable boolean" style="color:rgb(189, 147, 249);font-style:italic">true</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">)</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-x</span><span class="token plain"> /opt/local/bin/gpg </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">&amp;&amp;</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">GPG_BIN</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"/opt/local/bin/gpg"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-x</span><span class="token plain"> /opt/homebrew/bin/gpg </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">&amp;&amp;</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">GPG_BIN</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"/opt/homebrew/bin/gpg"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$GPG_BIN</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> --list-secret-keys --keyid-format LONG</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># 检查工作树；正式发布建议使用可追溯的 commit/tag</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> status </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--short</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># 404 表示该版本尚未出现在 Central；200 时必须换新版本</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-sS</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-o</span><span class="token plain"> /dev/null </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-w</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'%{http_code}\n'</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">"https://repo1.maven.org/maven2/cloud/changlu/agentforge/agentforge-ai-parent/</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${VERSION}</span><span class="token string" style="color:rgb(255, 121, 198)">/agentforge-ai-parent-</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${VERSION}</span><span class="token string" style="color:rgb(255, 121, 198)">.pom"</span><br></div></code></pre></div></div>
<p>At the same time, confirm in the Central Portal that the namespace is <strong>Verified</strong>, that <code>&lt;server id="central"&gt;</code> exists in <code>settings.xml</code>, and that the GPG public key has been uploaded to the keyserver.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="64-step-3-one-click-dry-run-no-upload">6.4 Step 3: One-Click Dry Run (No Upload)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#64-step-3-one-click-dry-run-no-upload" class="hash-link" aria-label="Direct link to 6.4 Step 3: One-Click Dry Run (No Upload)" title="Direct link to 6.4 Step 3: One-Click Dry Run (No Upload)" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">bin/local/release.sh </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$VERSION</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--settings</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$SETTINGS_FILE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  --dry-run</span><br></div></code></pre></div></div>
<p>When it runs, the script prompts:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">GPG Passphrase (input hidden):</span><br></div></code></pre></div></div>
<p>Enter the Passphrase you set when creating the current GPG private key. The input is not echoed and does not enter shell history. On success you should see:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">[INFO] BUILD SUCCESS</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">&gt;&gt; Dry run succeeded: artifacts were built and signed; nothing was uploaded.</span><br></div></code></pre></div></div>
<p>The result of this actual run: all 10 modules were <code>SUCCESS</code>, and all 28 actually generated <code>.asc</code> files passed local signature verification.</p>
<blockquote>
<p>Experience: do not use <code>-Dgpg.skip=true</code> for the final dry run; that can only verify the POM and artifact structure and cannot prove that the real signing pipeline is working.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="65-step-4-one-click-upload-recommended-for-the-first-time">6.5 Step 4: One-Click Upload (Recommended for the First Time)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#65-step-4-one-click-upload-recommended-for-the-first-time" class="hash-link" aria-label="Direct link to 6.5 Step 4: One-Click Upload (Recommended for the First Time)" title="Direct link to 6.5 Step 4: One-Click Upload (Recommended for the First Time)" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">bin/local/release.sh </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$VERSION</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--settings</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$SETTINGS_FILE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><br></div></code></pre></div></div>
<p>This mode runs <code>clean deploy</code>, but the POM has <code>autoPublish=false</code>, so it is only uploaded to the Central Portal and is not made public immediately. Success indicators:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">[INFO] BUILD SUCCESS</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">&gt;&gt; Central deployment was uploaded. Review and publish it in the Portal:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   https://central.sonatype.com/publishing/deployments</span><br></div></code></pre></div></div>
<p>The Bundle check results before this actual upload:</p>
<ul>
<li class="">Bundle size about <code>1.8 MB</code>;</li>
<li class=""><code>168</code> entries in total;</li>
<li class="">Exactly 10 public artifacts;</li>
<li class="">Does not include examples / studio;</li>
<li class="">POM, main JAR, sources JAR, Javadoc JAR, and the corresponding <code>.asc</code> files are all present.</li>
</ul>
<p><strong>Demonstration example:</strong></p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># 打开AgentForge</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">cd</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/Users/edy/changlu_workspace/mymd/我的开源项目/AgentForge"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># 执行一键推送</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">bin/local/release.sh </span><span class="token number">1.0</span><span class="token plain">.0 </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--settings</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/Users/edy/changlu_workspace/mymd/我的开源项目/maven/settings.xml"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    --dry-run</span><br></div></code></pre></div></div>
<p>The <strong>Passphrase</strong> entered is <strong>the key you set when installing GPG yourself</strong> ~</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="66-step-5-portal-review-and-publish">6.6 Step 5: Portal Review and Publish<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#66-step-5-portal-review-and-publish" class="hash-link" aria-label="Direct link to 6.6 Step 5: Portal Review and Publish" title="Direct link to 6.6 Step 5: Portal Review and Publish" translate="no">​</a></h2>
<p>Open <a href="https://central.sonatype.com/publishing/deployments" target="_blank" rel="noopener noreferrer" class="">https://central.sonatype.com/publishing/deployments</a>:</p>
<ol>
<li class="">
<p>Confirm that the deployment's version and artifact count are correct;</p>
</li>
<li class="">
<p>Wait for the status to become <strong>VALIDATED</strong>;</p>
</li>
<li class="">
<p>For the first release, it is recommended to review manually and then click <strong>Publish</strong>;</p>
<p><img decoding="async" loading="lazy" src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2024/202610050105391.png" alt="image-20261005010505202" class="img_ev3q"></p>
<p>At this point it enters the publishing process:</p>
<p><img decoding="async" loading="lazy" src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2024/202610050107677.png" alt="image-20261005010707580" class="img_ev3q"></p>
</li>
<li class="">
<p>Wait for the status to become <strong>PUBLISHED</strong>.</p>
</li>
</ol>
<p><img decoding="async" loading="lazy" src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2024/202610050116487.png" alt="image-20261005011610344" class="img_ev3q"></p>
<blockquote>
<p>Publish is an irreversible operation. A published GAV version on Central cannot be overwritten, so it is not recommended to use automatic Publish directly for the first time.</p>
</blockquote>
<p><strong>Waiting time after Publish:</strong></p>
<ul>
<li class="">After clicking <strong>Publish</strong>, <code>VALIDATED</code> usually moves to <code>PUBLISHING</code> immediately;</li>
<li class=""><code>PUBLISHING → PUBLISHED</code> generally takes a few minutes, commonly about <code>1～10</code> minutes;</li>
<li class="">After <code>PUBLISHED</code>, syncing to <code>repo1.maven.org</code> usually takes another few minutes;</li>
<li class="">The Central Search index usually updates within a few minutes after publishing, but may be later than the public repository.</li>
</ul>
<p>It is recommended to refresh the Deployments page every <code>1～2</code> minutes; do not upload repeatedly. After the status becomes <code>PUBLISHED</code>, you can check the public repository directly:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-sS</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-L</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-o</span><span class="token plain"> /dev/null </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-w</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'%{http_code}\n'</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">'https://repo1.maven.org/maven2/cloud/changlu/agentforge/agentforge-agent-core/1.0.0/agentforge-agent-core-1.0.0.pom'</span><br></div></code></pre></div></div>
<p>A return of <code>200</code> means it is publicly available; a return of <code>404</code> means to keep waiting for synchronization. If it remains at <code>PUBLISHING</code> for more than <code>30</code> minutes, first check <a href="https://status.maven.org/" target="_blank" rel="noopener noreferrer" class="">https://status.maven.org/</a>; if it is still not complete after more than <code>1</code> hour, record the deployment ID and contact <code>central-support@sonatype.com</code>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="67-step-6-optional-for-later-versions-one-click-automatic-publish">6.7 Step 6 (Optional for Later Versions): One-Click Automatic Publish<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#67-step-6-optional-for-later-versions-one-click-automatic-publish" class="hash-link" aria-label="Direct link to 6.7 Step 6 (Optional for Later Versions): One-Click Automatic Publish" title="Direct link to 6.7 Step 6 (Optional for Later Versions): One-Click Automatic Publish" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">VERSION</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"1.0.1"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">bin/local/release.sh </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$VERSION</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--settings</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$SETTINGS_FILE</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  --auto-publish</span><br></div></code></pre></div></div>
<p><code>--auto-publish</code> dynamically overrides the POM's <code>autoPublish=false</code> and uses <code>waitUntil=published</code> to wait for Central to finish publishing. It should only be used after the first manual release has been verified successfully.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="68-step-7-post-release-verification">6.8 Step 7: Post-Release Verification<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#68-step-7-post-release-verification" class="hash-link" aria-label="Direct link to 6.8 Step 7: Post-Release Verification" title="Direct link to 6.8 Step 7: Post-Release Verification" translate="no">​</a></h2>
<ul>
<li class="">
<p>The Portal Deployments status is <strong>PUBLISHED</strong>;</p>
</li>
<li class="">
<p>Search for the full groupId <code>cloud.changlu.agentforge</code> or the artifactId <code>agentforge-agent-core</code> at <a href="https://central.sonatype.com/" target="_blank" rel="noopener noreferrer" class="">https://central.sonatype.com/</a>;</p>
</li>
<li class="">
<p>Directly check <code>repo1.maven.org/maven2/&lt;groupId 路径&gt;/&lt;artifactId&gt;/&lt;version&gt;/</code>;</p>
</li>
<li class="">
<p>Actually reference it in a blank Maven project:</p>
<div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">cloud.changlu.agentforge</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">agentforge-agent-core</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">1.0.0</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><br></div></code></pre></div></div>
</li>
</ul>
<p>You can first use the HTTP status for a cache-free core verification:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-sS</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-L</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-o</span><span class="token plain"> /dev/null </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-w</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'%{http_code}\n'</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">'https://repo1.maven.org/maven2/cloud/changlu/agentforge/agentforge-agent-core/1.0.0/agentforge-agent-core-1.0.0.pom'</span><br></div></code></pre></div></div>
<ul>
<li class=""><code>200</code>: the artifact has entered the Maven Central public repository;</li>
<li class=""><code>404</code> and the Portal is <code>VALIDATED</code>: only the upload is complete; you still need to click <strong>Publish</strong> on the Deployments page;</li>
<li class="">The Portal is <code>PUBLISHING</code>: keep waiting; do not upload repeatedly;</li>
<li class="">The Portal is <code>PUBLISHED</code> but it is still <code>404</code>: the public repository is syncing; retry later;</li>
<li class=""><code>repo1.maven.org</code> already returns <code>200</code> but Central Search cannot find it: this is just a search index delay.</li>
</ul>
<blockquote>
<p>After the Portal shows PUBLISHED, syncing to Central Search and mirror repositories may still take tens of minutes; the Portal status and <code>repo1.maven.org</code> are authoritative. The script does not contain any credentials: the Central Token is read only from <code>settings.xml</code>, and the GPG Passphrase exists only in the current release process.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-faq-and-notes">7. FAQ and Notes<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#7-faq-and-notes" class="hash-link" aria-label="Direct link to 7. FAQ and Notes" title="Direct link to 7. FAQ and Notes" translate="no">​</a></h2>
<table><thead><tr><th>Issue</th><th>Description / Handling</th></tr></thead><tbody><tr><td>Namespace verification fails</td><td>Confirm that the TXT record is added on the <strong>root domain <code>changlu.cloud</code></strong>, that the value is exactly identical, and verify with <code>dig -t txt changlu.cloud</code></td></tr><tr><td><code>Invalid signature</code> / GPG error</td><td>Confirm the KEY_ID is correct, the public key has been uploaded, and <code>MAVEN_GPG_PASSPHRASE</code> is correct; gpg version ≥ 2.2</td></tr><tr><td><code>损坏的密码</code> / <code>Bad passphrase</code></td><td>The input must be the Passphrase set when creating the current GPG private key, not the Central Token, KEY_ID, fingerprint, or macOS password; once forgotten it cannot be recovered, and you must switch to a new key</td></tr><tr><td>bundle missing sources/javadoc</td><td>Check whether <code>-P release</code> took effect and whether javadoc was interrupted by source errors (<code>doclint=none</code> has been added)</td></tr><tr><td>POM missing metadata</td><td>Confirm the parent has <code>licenses</code>/<code>developers</code>/<code>scm</code> and each module has <code>name</code>/<code>description</code>/<code>url</code></td></tr><tr><td>Version already exists</td><td>Central <strong>cannot overwrite / delete</strong> a published version; you must switch to a new version number</td></tr><tr><td><code>-SNAPSHOT</code> rejected</td><td>An official release must be non-SNAPSHOT; snapshots go through a separate channel and must not be mixed</td></tr><tr><td>Not findable after upload</td><td>Synchronization has a delay; first check the Published status in Portal Deployments</td></tr><tr><td>Example modules got packaged</td><td>Confirm <code>excludeArtifacts</code> took effect, or first use <code>--dry-run</code> to check the bundle contents</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-summary">8. Summary<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#8-summary" class="hash-link" aria-label="Direct link to 8. Summary" title="Direct link to 8. Summary" translate="no">​</a></h2>
<p>The complete path to publishing to Maven Central can be summarized as:</p>
<ol>
<li class=""><strong>Choose a namespace</strong>: without a domain use <code>io.github.&lt;username&gt;</code>; with a domain use the reverse domain (this scheme chooses <code>cloud.changlu</code>);</li>
<li class=""><strong>Pass verification</strong>: for the domain approach add a DNS TXT; for the GitHub approach create a public repository;</li>
<li class=""><strong>Prepare keys and passphrase</strong>: GPG key pair + Portal User Token + <code>settings.xml</code>;</li>
<li class=""><strong>Modify the build</strong>: add metadata and a <code>release</code> profile (sources / javadoc / gpg / central) in the parent;</li>
<li class=""><strong>Run the release</strong>: <code>--dry-run</code> dry run → formal <code>deploy</code> → Portal Publish → verify the dependency;</li>
<li class=""><strong>Stay sustainable</strong>: use <code>release.sh</code> to solidify the process, increment the version number, and never overwrite.</li>
</ol>
<p>At this point, AgentForge's library modules can be imported directly by Java developers all over the world through a single dependency coordinate.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://agentforge-tech.github.io/AgentForge/en/blog/%E7%95%AA%E5%A4%96/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%B9%8BAgentForge%E5%8F%91%E5%B8%83%E5%88%B0MavenCentral%E5%AE%98%E6%96%B9%E4%BB%93%E5%BA%93%E5%AE%9E%E6%88%98#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<p>[1]. <a href="https://central.sonatype.org/" target="_blank" rel="noopener noreferrer" class="">Sonatype Central Portal Official Documentation</a></p>
<p>[2]. <a href="https://central.sonatype.org/publish/requirements/coordinates/" target="_blank" rel="noopener noreferrer" class="">Choosing your Coordinates (groupId namespace)</a></p>
<p>[3]. <a href="https://central.sonatype.org/publish/requirements/" target="_blank" rel="noopener noreferrer" class="">Requirements (sources/javadoc, GPG, POM metadata)</a></p>
<p>[4]. <a href="https://central.sonatype.org/publish/publish-portal-maven/" target="_blank" rel="noopener noreferrer" class="">Publishing By Using the Maven Plugin</a></p>
<p>[5]. <a href="https://central.sonatype.org/publish/generate-portal-token/" target="_blank" rel="noopener noreferrer" class="">Generating a Portal Token for Publishing</a></p>
<p>[6]. <a href="https://central.sonatype.org/faq/how-to-set-txt-record/" target="_blank" rel="noopener noreferrer" class="">How do I set the TXT record needed to prove ownership of my Web Domain?</a></p>
<p>[7]. <a href="https://central.sonatype.org/publish/requirements/gpg/" target="_blank" rel="noopener noreferrer" class="">GPG Requirements</a></p>
<p>[8]. <a href="https://central.sonatype.com/artifact/org.sonatype.central/central-publishing-maven-plugin" target="_blank" rel="noopener noreferrer" class="">central-publishing-maven-plugin (Maven Central Artifact)</a></p>
<br>
<hr>
<p>Compiled by: Changlu　Created: 2026.10.4　Updated: 2026.10.5</p>]]></content:encoded>
            <category>AgentForge</category>
            <category>Extra</category>
            <category>Engineering</category>
        </item>
        <item>
            <title><![CDATA[MCP Protocol to FunctionCall Protocol 02: MCP Framework Research and AgentForge Integration Implementation]]></title>
            <link>https://agentforge-tech.github.io/AgentForge/en/blog/02、通用ReActagent层/functioncall核心扩展业务tool/mcp/MCP协议转换FunctionCall协议02、MCP框架调研与AgentForge接入实现</link>
            <guid>https://agentforge-tech.github.io/AgentForge/en/blog/02、通用ReActagent层/functioncall核心扩展业务tool/mcp/MCP协议转换FunctionCall协议02、MCP框架调研与AgentForge接入实现</guid>
            <pubDate>Sat, 10 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[This article unfolds progressively along the line of "background/problem introduction → core concepts → mainstream framework research → implementation approach → hands-on code → verification testing → summary", in seven parts.]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>This article unfolds progressively along the line of "<strong>background/problem introduction → core concepts → mainstream framework research → implementation approach → hands-on code → verification testing → summary</strong>", in seven parts.</p>
</blockquote>
<!-- -->
<br>
<p>One-sentence conclusion: Among the three parties, <strong>only LangChain4j has its own in-house protocol stack; both Spring AI and AgentScope Java are based on the official MCP Java SDK</strong>; while <strong>AgentForge chooses "in-house core protocol stack + optional official SDK bridge"</strong>. Let's clarify the full story behind this conclusion below.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-background-and-problem-introduction">1. Background and Problem Introduction<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#1-background-and-problem-introduction" class="hash-link" aria-label="Direct link to 1. Background and Problem Introduction" title="Direct link to 1. Background and Problem Introduction" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="11-scenario-drive-a-real-integration-challenge">1.1 Scenario Drive: A Real Integration Challenge<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#11-scenario-drive-a-real-integration-challenge" class="hash-link" aria-label="Direct link to 1.1 Scenario Drive: A Real Integration Challenge" title="Direct link to 1.1 Scenario Drive: A Real Integration Challenge" translate="no">​</a></h2>
<p>When planning MCP ecosystem integration for AgentForge, we ran into this concrete problem:</p>
<blockquote>
<p>The team hoped AgentForge's <code>ReActAgent</code> could directly consume the ready-made tools in the MCP ecosystem (filesystem, git, fetch, database…). But AgentForge has a hard constraint—<strong>Java 8 compatibility, zero third-party dependencies in the core</strong>. And looking across the Java ecosystem, both Spring AI and AgentScope chose the official MCP Java SDK, yet that SDK requires <strong>JDK 17+</strong> and also pulls in Jackson, Reactor, and SLF4J.<br>
<!-- -->So the question arose: <strong>Should we follow the official SDK or not?</strong></p>
</blockquote>
<p>This is not a confusion unique to AgentForge. Any framework oriented toward "low JDK / zero dependencies" will hit the same wall when integrating MCP.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="12-problem-guidance-what-do-we-need-to-answer">1.2 Problem Guidance: What Do We Need to Answer?<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#12-problem-guidance-what-do-we-need-to-answer" class="hash-link" aria-label="Direct link to 1.2 Problem Guidance: What Do We Need to Answer?" title="Direct link to 1.2 Problem Guidance: What Do We Need to Answer?" translate="no">​</a></h2>
<ol>
<li class=""><strong>For the three mainstream Java frameworks, do they support stdio and HTTP? To what extent?</strong><br></li>
<li class=""><strong>Is their protocol "hand-written by themselves" or "based on the official MCP Java SDK"?</strong><br></li>
<li class=""><strong>How should AgentForge choose? In-house or official? To what extent should it support?</strong><br></li>
<li class=""><strong>After implementation, how do we unify it with AgentForge's existing <code>http</code> / <code>local</code> tool modes?</strong></li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="13-research-scope-and-comparison-baseline">1.3 Research Scope and Comparison Baseline<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#13-research-scope-and-comparison-baseline" class="hash-link" aria-label="Direct link to 1.3 Research Scope and Comparison Baseline" title="Direct link to 1.3 Research Scope and Comparison Baseline" translate="no">​</a></h2>
<p>Research targets: <strong>LangChain4j</strong>, <strong>Spring AI</strong>, <strong>AgentScope Java</strong>. The comparison baseline is the official <strong>MCP Java SDK</strong>.</p>
<table><thead><tr><th>Item</th><th>Official MCP Java SDK</th></tr></thead><tbody><tr><td>Coordinates</td><td><code>io.modelcontextprotocol.sdk:mcp</code> (BOM: <code>mcp-bom</code>)</td></tr><tr><td>Current version</td><td><strong>2.0.1</strong></td></tr><tr><td>JDK</td><td><strong>17+</strong></td></tr><tr><td>Programming model</td><td>Synchronous + Asynchronous (Reactor)</td></tr><tr><td>JSON / Logging</td><td>Jackson (pluggable) / SLF4J</td></tr><tr><td>Client transport</td><td><strong>STDIO, Streamable HTTP, SSE (legacy)</strong></td></tr><tr><td>Server transport</td><td><strong>STDIO, Streamable-HTTP, Stateless Streamable-HTTP, SSE</strong></td></tr><tr><td>Architecture layering</td><td>Client/Server → Session → Transport, three layers</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-core-concepts">2. Core Concepts<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#2-core-concepts" class="hash-link" aria-label="Direct link to 2. Core Concepts" title="Direct link to 2. Core Concepts" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-two-technical-routes-official-sdk-vs-in-house">2.1 Two Technical Routes: Official SDK vs In-House<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#21-two-technical-routes-official-sdk-vs-in-house" class="hash-link" aria-label="Direct link to 2.1 Two Technical Routes: Official SDK vs In-House" title="Direct link to 2.1 Two Technical Routes: Official SDK vs In-House" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="211-route-one-based-on-the-official-java-sdk">2.1.1 Route One: Based on the Official Java SDK<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#211-route-one-based-on-the-official-java-sdk" class="hash-link" aria-label="Direct link to 2.1.1 Route One: Based on the Official Java SDK" title="Direct link to 2.1.1 Route One: Based on the Official Java SDK" translate="no">​</a></h3>
<p>Protocol details (JSON-RPC, version negotiation, transport framing) are maintained by the official team, and the framework only does <strong>thin wrapping + ecosystem integration</strong>.</p>
<ul>
<li class=""><strong>Pros</strong>: Worry-free, easy to pass conformance tests, full-featured, fast to follow the spec.</li>
<li class=""><strong>Downside explanation</strong>: JDK 17+, pulls in Jackson / Reactor / SLF4J, hard to embed into low-JDK / zero-dependency projects.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="212-route-two-in-house-protocol-stack">2.1.2 Route Two: In-House Protocol Stack<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#212-route-two-in-house-protocol-stack" class="hash-link" aria-label="Direct link to 2.1.2 Route Two: In-House Protocol Stack" title="Direct link to 2.1.2 Route Two: In-House Protocol Stack" translate="no">​</a></h3>
<p>The framework implements <code>McpTransport</code> / <code>McpClient</code> / JSON-RPC routing itself.</p>
<ul>
<li class=""><strong>Pros</strong>: Full control over dependencies and API, can be compatible with lower JDKs, zero extra dependencies.</li>
<li class=""><strong>Downside explanation</strong>: Must follow spec revisions on your own, and do dual-era compatibility and conformance verification.</li>
</ul>
<blockquote>
<p><strong>Key point</strong>: These two routes have no absolute superiority; it only depends on the framework's own <strong>constraint orientation</strong>—which is exactly the root cause of the three-way divergence and also the root cause of AgentForge's decision.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-the-official-sdks-three-layer-architecture-for-reference">2.2 The Official SDK's Three-Layer Architecture (for Reference)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#22-the-official-sdks-three-layer-architecture-for-reference" class="hash-link" aria-label="Direct link to 2.2 The Official SDK's Three-Layer Architecture (for Reference)" title="Direct link to 2.2 The Official SDK's Three-Layer Architecture (for Reference)" translate="no">​</a></h2>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Client/Server layer   Protocol operations (listTools / callTool / initialize)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ↓</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Session layer         Communication mode and connection state</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ↓</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Transport layer       JSON-RPC send/receive and serialization (STDIO / HTTP / SSE)</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="23-three-way-overview">2.3 Three-Way Overview<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#23-three-way-overview" class="hash-link" aria-label="Direct link to 2.3 Three-Way Overview" title="Direct link to 2.3 Three-Way Overview" translate="no">​</a></h2>
<table><thead><tr><th>Framework</th><th>stdio</th><th>HTTP</th><th>Implementation</th><th>Official SDK</th></tr></thead><tbody><tr><td><strong>LangChain4j</strong></td><td>✅</td><td>✅ Streamable HTTP (+ WebSocket / Docker)</td><td><strong>In-house protocol stack</strong></td><td>❌ Not used</td></tr><tr><td><strong>Spring AI</strong></td><td>✅</td><td>✅ Streamable / Stateless / SSE (WebMVC / WebFlux)</td><td><strong>Based on official SDK</strong></td><td>✅ Depends on 1.0+</td></tr><tr><td><strong>AgentScope Java</strong></td><td>✅</td><td>✅ Streamable HTTP / SSE</td><td><strong>Based on official SDK</strong></td><td>✅ Depends</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-mainstream-framework-research">3. Mainstream Framework Research<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#3-mainstream-framework-research" class="hash-link" aria-label="Direct link to 3. Mainstream Framework Research" title="Direct link to 3. Mainstream Framework Research" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-langchain4j-in-house-protocol-stack">3.1 LangChain4j: In-House Protocol Stack<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#31-langchain4j-in-house-protocol-stack" class="hash-link" aria-label="Direct link to 3.1 LangChain4j: In-House Protocol Stack" title="Direct link to 3.1 LangChain4j: In-House Protocol Stack" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="311-did-it-actually-use-the-official-sdk">3.1.1 Did It Actually Use the Official SDK?<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#311-did-it-actually-use-the-official-sdk" class="hash-link" aria-label="Direct link to 3.1.1 Did It Actually Use the Official SDK?" title="Direct link to 3.1.1 Did It Actually Use the Official SDK?" translate="no">​</a></h3>
<p>MCP capabilities are in the <code>dev.langchain4j:langchain4j-mcp</code> module. Checking its <code>pom.xml</code> dependencies: <code>langchain4j</code>, <code>langchain4j-core</code>, <code>jackson-databind</code>, <code>langchain4j-http-client(-jdk)</code>, <code>langchain4j-open-ai</code>. <strong>There is no <code>io.modelcontextprotocol.sdk</code></strong>—meaning the protocol is self-implemented.</p>
<blockquote>
<p><strong>Key point</strong>: It is the <strong>only self-developed</strong> one among the three (its own <code>McpTransport</code> / <code>DefaultMcpClient</code> / <code>McpOperationHandler</code>).</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="312-stdio-support">3.1.2 stdio Support<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#312-stdio-support" class="hash-link" aria-label="Direct link to 3.1.2 stdio Support" title="Direct link to 3.1.2 stdio Support" translate="no">​</a></h3>
<ul>
<li class=""><code>StdioMcpTransport</code>: <code>ProcessBuilder</code> spawns a child process, reading and writing JSON-RPC line by line.</li>
<li class="">A separate module <code>langchain4j-mcp-docker</code> provides <code>DockerMcpTransport</code> (containerized stdio server).</li>
<li class="">The server side (building a stdio server) is placed in <strong>LangChain4j Community</strong>.</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="313-http-support">3.1.3 HTTP Support<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#313-http-support" class="hash-link" aria-label="Direct link to 3.1.3 HTTP Support" title="Direct link to 3.1.3 HTTP Support" translate="no">​</a></h3>
<ul>
<li class=""><code>StreamableHttpMcpTransport</code>: based on the JDK <code>HttpClient</code>, POST to a single endpoint; optional <code>subsidiaryChannel(true)</code> opens a GET SSE subsidiary channel (legacy).</li>
<li class=""><code>HttpMcpTransport</code>: old HTTP+SSE, already <code>@Deprecated(forRemoval)</code>.</li>
<li class=""><code>WebSocketMcpTransport</code>: non-standard extra transport, compatible with the Quarkus MCP Server extension.</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="314-protocol-versions-and-tool-governance">3.1.4 Protocol Versions and Tool Governance<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#314-protocol-versions-and-tool-governance" class="hash-link" aria-label="Direct link to 3.1.4 Protocol Versions and Tool Governance" title="Direct link to 3.1.4 Protocol Versions and Tool Governance" translate="no">​</a></h3>
<ul>
<li class="">Supports both <strong>legacy (2025-11-25) and modern (2026-07-28)</strong>, with automatic detection by default (<code>server/discover</code> → fall back to <code>initialize</code> on failure).</li>
<li class="">Tool capabilities: <code>McpToolProvider</code> supports filtering by name, <code>toolNameMapper</code>, <code>toolSpecificationMapper</code>, multi-client aggregation; <code>DefaultMcpClient</code> has a built-in tool list cache; supports <code>x-mcp-header</code>, <code>_meta</code> injection, resource subscription.</li>
<li class="">Also has a <strong>MCP Registry</strong> read-only client.</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-spring-ai-based-on-the-official-java-sdk">3.2 Spring AI: Based on the Official Java SDK<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#32-spring-ai-based-on-the-official-java-sdk" class="hash-link" aria-label="Direct link to 3.2 Spring AI: Based on the Official Java SDK" title="Direct link to 3.2 Spring AI: Based on the Official Java SDK" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="321-did-it-actually-use-the-official-sdk">3.2.1 Did It Actually Use the Official SDK?<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#321-did-it-actually-use-the-official-sdk" class="hash-link" aria-label="Direct link to 3.2.1 Did It Actually Use the Official SDK?" title="Direct link to 3.2.1 Did It Actually Use the Official SDK?" translate="no">​</a></h3>
<p>Spring AI MCP <strong>explicitly adopts the official MCP Java SDK</strong>, and its documentation directly presents the SDK's three-layer architecture. Since <strong>Spring AI 2.0</strong>, the official SDK <strong>1.0.0+</strong> is required.</p>
<blockquote>
<p><strong>Note (migration point)</strong>: <code>mcp-spring-webflux</code> / <code>mcp-spring-webmvc</code> <strong>migrated</strong> from <code>io.modelcontextprotocol.sdk</code> <strong>to</strong> <code>org.springframework.ai</code>; the relevant transport class package names also changed from <code>io.modelcontextprotocol.*</code> to <code>org.springframework.ai.mcp.*</code>.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="322-stdio-support">3.2.2 stdio Support<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#322-stdio-support" class="hash-link" aria-label="Direct link to 3.2.2 stdio Support" title="Direct link to 3.2.2 stdio Support" translate="no">​</a></h3>
<ul>
<li class="">Client: <code>spring-ai-starter-mcp-client</code> provides STDIO.</li>
<li class="">Server: <code>spring-ai-starter-mcp-server</code> (<code>spring.ai.mcp.server.stdio=true</code>).</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="323-http-support">3.2.3 HTTP Support<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#323-http-support" class="hash-link" aria-label="Direct link to 3.2.3 HTTP Support" title="Direct link to 3.2.3 HTTP Support" translate="no">​</a></h3>
<ul>
<li class="">Client: <code>spring-ai-starter-mcp-client</code> (Servlet version Streamable-HTTP / Stateless / SSE), <code>spring-ai-starter-mcp-client-webflux</code> (WebFlux version).</li>
<li class="">Server: <code>spring-ai-starter-mcp-server-webmvc</code> / <code>-webflux</code>, switched with <code>spring.ai.mcp.server.protocol = SSE | STREAMABLE | STATELESS</code>.</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="324-annotations-and-governance">3.2.4 Annotations and Governance<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#324-annotations-and-governance" class="hash-link" aria-label="Direct link to 3.2.4 Annotations and Governance" title="Direct link to 3.2.4 Annotations and Governance" translate="no">​</a></h3>
<ul>
<li class="">Server annotations: <code>@McpTool</code>, <code>@McpResource</code>, <code>@McpPrompt</code>, <code>@McpComplete</code>.</li>
<li class="">Client annotations: <code>@McpLogging</code>, <code>@McpSampling</code>, <code>@McpElicitation</code>, <code>@McpProgress</code>.</li>
<li class="">Tool adaptation: <code>SyncMcpToolCallback</code> / <code>AsyncMcpToolCallback</code>, <code>SyncMcpToolCallbackProvider</code>, <code>McpToolUtils</code>.</li>
</ul>
<blockquote>
<p><strong>Key point</strong>: Spring AI does not implement the protocol itself, but rather <strong>thin-wraps the official SDK + fills out the Spring Boot experience</strong> (Starter / annotations / WebMVC·WebFlux transports).</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-agentscope-java-based-on-the-official-java-sdk">3.3 AgentScope Java: Based on the Official Java SDK<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#33-agentscope-java-based-on-the-official-java-sdk" class="hash-link" aria-label="Direct link to 3.3 AgentScope Java: Based on the Official Java SDK" title="Direct link to 3.3 AgentScope Java: Based on the Official Java SDK" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="331-did-it-actually-use-the-official-sdk">3.3.1 Did It Actually Use the Official SDK?<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#331-did-it-actually-use-the-official-sdk" class="hash-link" aria-label="Direct link to 3.3.1 Did It Actually Use the Official SDK?" title="Direct link to 3.3.1 Did It Actually Use the Official SDK?" translate="no">​</a></h3>
<p>AgentScope Java's <code>McpClientBuilder</code> internally <strong>reuses the transport and client classes of the official <code>io.modelcontextprotocol</code> SDK</strong>, ultimately wrapping them into <code>McpAsyncClient</code> / <code>McpSyncClient</code>, and injects AgentScope's version and User-Agent.</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="332-stdio-support">3.3.2 stdio Support<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#332-stdio-support" class="hash-link" aria-label="Direct link to 3.3.2 stdio Support" title="Direct link to 3.3.2 stdio Support" translate="no">​</a></h3>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">McpClientWrapper</span><span class="token plain"> client </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">McpClientBuilder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">create</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"filesystem-mcp"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">stdioTransport</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"npx"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"-y"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"@modelcontextprotocol/server-filesystem"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/tmp"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">buildAsync</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">block</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="333-http-support">3.3.3 HTTP Support<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#333-http-support" class="hash-link" aria-label="Direct link to 3.3.3 HTTP Support" title="Direct link to 3.3.3 HTTP Support" translate="no">​</a></h3>
<ul>
<li class=""><code>sseTransport(url)</code>: SSE (stateful streaming).</li>
<li class=""><code>streamableHttpTransport(url)</code>: Streamable HTTP (stateless streaming).</li>
<li class="">Supports <code>.header(...)</code> / <code>.queryParam(...)</code>, <code>.timeout(...)</code>, <code>.initializationTimeout(...)</code>, <code>.protocolVersions(...)</code>, <code>elicitation</code> callback.</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="334-tool-governance">3.3.4 Tool Governance<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#334-tool-governance" class="hash-link" aria-label="Direct link to 3.3.4 Tool Governance" title="Direct link to 3.3.4 Tool Governance" translate="no">​</a></h3>
<ul>
<li class=""><code>Toolkit.registerMcpClient(...)</code> for unified registration; <code>enableTools</code> / <code>disableTools</code> whitelist and blacklist; <code>group(...)</code> tool grouping; <code>removeMcpClient(...)</code> dynamic removal.</li>
<li class="">Namespace convention: <code>mcp__{server}__{tool}</code>.</li>
<li class="">Also has the Higress AI gateway extension (semantic tool retrieval).</li>
</ul>
<blockquote>
<p><strong>Note</strong>: By default it only declares <code>2024-11-05</code>; connecting to newer servers requires explicitly <code>protocolVersions(...)</code>, otherwise it reports "Unsupported protocol version".</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="34-horizontal-comparison">3.4 Horizontal Comparison<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#34-horizontal-comparison" class="hash-link" aria-label="Direct link to 3.4 Horizontal Comparison" title="Direct link to 3.4 Horizontal Comparison" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="341-stdio--http-support-matrix">3.4.1 stdio / HTTP Support Matrix<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#341-stdio--http-support-matrix" class="hash-link" aria-label="Direct link to 3.4.1 stdio / HTTP Support Matrix" title="Direct link to 3.4.1 stdio / HTTP Support Matrix" translate="no">​</a></h3>
<table><thead><tr><th>Capability</th><th>LangChain4j</th><th>Spring AI</th><th>AgentScope Java</th></tr></thead><tbody><tr><td>Client stdio</td><td>✅</td><td>✅</td><td>✅</td></tr><tr><td>Client Streamable HTTP</td><td>✅</td><td>✅ (Servlet / WebFlux)</td><td>✅</td></tr><tr><td>Client SSE (legacy)</td><td>✅ (being deprecated)</td><td>✅</td><td>✅</td></tr><tr><td>Server stdio</td><td>⚠️ (Community)</td><td>✅</td><td>— (client-oriented)</td></tr><tr><td>Server Streamable HTTP</td><td>⚠️ (Community)</td><td>✅ (WebMVC / WebFlux / Stateless)</td><td>—</td></tr><tr><td>Extra transport</td><td>WebSocket / Docker</td><td>—</td><td>—</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="342-implementation-and-constraints">3.4.2 Implementation and Constraints<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#342-implementation-and-constraints" class="hash-link" aria-label="Direct link to 3.4.2 Implementation and Constraints" title="Direct link to 3.4.2 Implementation and Constraints" translate="no">​</a></h3>
<table><thead><tr><th>Dimension</th><th>LangChain4j</th><th>Spring AI</th><th>AgentScope Java</th></tr></thead><tbody><tr><td>Protocol implementation</td><td><strong>In-house</strong></td><td>Official SDK</td><td>Official SDK</td></tr><tr><td>Official SDK version</td><td>Not depended on</td><td>1.0.0+</td><td>Depends on <code>io.modelcontextprotocol</code></td></tr><tr><td>JDK/dependency constraint</td><td>Follows LangChain4j</td><td>Spring + SDK (JDK17+)</td><td>SDK (JDK17+)</td></tr><tr><td>Protocol version</td><td>legacy + modern auto-detection</td><td>Follows SDK</td><td>Requires explicit version declaration</td></tr><tr><td>Tool governance</td><td>Filter / rename / treat resources as tools</td><td>Starter + annotations</td><td>Filter / group / gateway retrieval</td></tr><tr><td>Registry</td><td>✅ Read-only client</td><td>Partial</td><td>—</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="35-comparative-analysis-and-selection-recommendations">3.5 Comparative Analysis and Selection Recommendations<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#35-comparative-analysis-and-selection-recommendations" class="hash-link" aria-label="Direct link to 3.5 Comparative Analysis and Selection Recommendations" title="Direct link to 3.5 Comparative Analysis and Selection Recommendations" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="351-three-selection-scenarios">3.5.1 Three Selection Scenarios<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#351-three-selection-scenarios" class="hash-link" aria-label="Direct link to 3.5.1 Three Selection Scenarios" title="Direct link to 3.5.1 Three Selection Scenarios" translate="no">​</a></h3>
<ul>
<li class=""><strong>Option One: Already a Spring Boot application</strong> → Spring AI. Official SDK + Starter is the least hassle, but you're bound to the Spring ecosystem.</li>
<li class=""><strong>Option Two: Multi-Agent orchestration / need tool governance and gateway</strong> → AgentScope Java. Tool grouping, namespaces, and Higress gateway retrieval are the most complete.</li>
<li class=""><strong>Option Three: Already using LangChain4j or need to escape official SDK constraints</strong> → LangChain4j. In-house protocol stack, controllable dependencies.</li>
</ul>
<blockquote>
<p><strong>Downside explanation</strong>: All three directly or indirectly <strong>require JDK 17+ and pull in third-party libraries</strong>. Any project that is "low JDK / zero dependencies" satisfies none of the three—the only option is in-house.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="352-summary">3.5.2 Summary<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#352-summary" class="hash-link" aria-label="Direct link to 3.5.2 Summary" title="Direct link to 3.5.2 Summary" translate="no">​</a></h3>
<ul>
<li class=""><strong>At the transport level</strong>: All three support stdio and HTTP (Streamable HTTP).</li>
<li class=""><strong>At the implementation level</strong>: <strong>LangChain4j is in-house, Spring AI and AgentScope are based on the official SDK</strong>.</li>
<li class=""><strong>Inference</strong>: The official SDK is the de facto standard, but it raises the JDK bar to 17—which is exactly where AgentForge needs to make a separate decision.</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-agentforge-implementation-approach">4. AgentForge Implementation Approach<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#4-agentforge-implementation-approach" class="hash-link" aria-label="Direct link to 4. AgentForge Implementation Approach" title="Direct link to 4. AgentForge Implementation Approach" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="41-agentforge-current-state-and-constraints">4.1 AgentForge Current State and Constraints<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#41-agentforge-current-state-and-constraints" class="hash-link" aria-label="Direct link to 4.1 AgentForge Current State and Constraints" title="Direct link to 4.1 AgentForge Current State and Constraints" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="411-hard-constraints">4.1.1 Hard Constraints<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#411-hard-constraints" class="hash-link" aria-label="Direct link to 4.1.1 Hard Constraints" title="Direct link to 4.1.1 Hard Constraints" translate="no">​</a></h3>
<table><thead><tr><th>Constraint</th><th>Description</th></tr></thead><tbody><tr><td>JDK</td><td><code>maven.compiler.source/target = 8</code> (API oriented toward Java 8)</td></tr><tr><td>Dependencies</td><td>Core module <strong>zero third-party dependencies</strong>: no Jackson / OkHttp / SLF4J / Lombok / Reactor</td></tr><tr><td>JSON</td><td>Built-in <code>cloud.changlu.agentforge.model.internal.json.Json</code></td></tr><tr><td>HTTP</td><td>Built-in <code>HttpTransport</code> / <code>HttpRequest</code> / <code>HttpResponse</code> / <code>JdkHttpTransport</code></td></tr><tr><td>Logging</td><td><code>java.util.logging</code></td></tr></tbody></table>
<blockquote>
<p><strong>Key point</strong>: The official MCP Java SDK requires <strong>JDK 17+</strong> and pulls in <strong>Jackson + Reactor + SLF4J</strong>, which <strong>directly conflicts</strong> with AgentForge's core constraints.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="412-reusable-tool-abstractions">4.1.2 Reusable Tool Abstractions<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#412-reusable-tool-abstractions" class="hash-link" aria-label="Direct link to 4.1.2 Reusable Tool Abstractions" title="Direct link to 4.1.2 Reusable Tool Abstractions" translate="no">​</a></h3>
<table><thead><tr><th>Existing type</th><th>Role</th><th>Correspondence with MCP</th></tr></thead><tbody><tr><td><code>ToolExecutor</code> (<code>execute</code> / <code>executeWithResult</code>)</td><td>Tool execution interface</td><td><code>tools/call</code></td></tr><tr><td><code>ToolExecutionRequest</code></td><td><code>name</code> + <code>arguments</code>(JSON)</td><td><code>tools/call</code> input</td></tr><tr><td><code>ToolExecutionResult</code></td><td><code>text</code> / <code>result</code> / <code>isError</code></td><td><code>content</code> / <code>structuredContent</code> / <code>isError</code></td></tr><tr><td><code>ToolSpecification</code></td><td>Tool declaration</td><td>the tool of <code>tools/list</code></td></tr><tr><td><code>ToolParameters</code></td><td>Raw JSON Schema (Map)</td><td><code>inputSchema</code></td></tr><tr><td><code>ToolService</code></td><td>Tool registration and loop</td><td>tool table</td></tr></tbody></table>
<blockquote>
<p><strong>Key point</strong>: MCP and AgentForge's tool abstractions are <strong>naturally isomorphic</strong>, and the integration point is the implementation of <code>ToolExecutor</code>.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="413-existing-mode-conventions">4.1.3 Existing Mode Conventions<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#413-existing-mode-conventions" class="hash-link" aria-label="Direct link to 4.1.3 Existing Mode Conventions" title="Direct link to 4.1.3 Existing Mode Conventions" translate="no">​</a></h3>
<p><code>agent-core</code> already has two tool modes, following the same "factory + executor + subpackage" pattern:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">tool/local/   LocalToolFactory + LocalToolExecutor + support/</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">tool/http/    HttpToolFactory  + HttpToolExecutor  + domain/ enums/ support/ parser/</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">tool/mcp/     (newly added in this article, aligned with the above conventions)</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="42-implementation-approach-official-sdk-vs-in-house">4.2 Implementation Approach: Official SDK vs In-House<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#42-implementation-approach-official-sdk-vs-in-house" class="hash-link" aria-label="Direct link to 4.2 Implementation Approach: Official SDK vs In-House" title="Direct link to 4.2 Implementation Approach: Official SDK vs In-House" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="421-option-comparison">4.2.1 Option Comparison<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#421-option-comparison" class="hash-link" aria-label="Direct link to 4.2.1 Option Comparison" title="Direct link to 4.2.1 Option Comparison" translate="no">​</a></h3>
<p><strong>Option One: Based on the official MCP Java SDK</strong></p>
<ul>
<li class="">Pros: Protocol maintained officially, easy to pass conformance tests, full-featured.</li>
<li class="">Downside explanation: <strong>JDK 17+</strong>, pulls in Jackson / Reactor / SLF4J, breaking the core's zero dependencies; requires an extra bridge layer to adapt <code>ToolExecutor</code>.</li>
</ul>
<p><strong>Option Two: In-house protocol stack</strong></p>
<ul>
<li class="">Pros: <strong>Java 8 compatible, zero extra dependencies</strong>, seamless with <code>ToolExecutor</code>, reuses the built-in <code>Json</code> and <code>HttpTransport</code>.</li>
<li class="">Downside explanation: Must follow spec revisions on your own, and do dual-era compatibility and conformance verification.</li>
</ul>
<p><strong>Option Three: Hybrid (in-house core + official bridge)</strong></p>
<ul>
<li class="">The core (<code>agentforge-agent-core</code>) uses an in-house protocol stack, maintaining zero dependencies (<strong>already delivered</strong>);</li>
<li class="">Additionally, an <strong>optional module</strong> <code>agentforge-mcp-sdk-bridge</code> (JDK 17), wrapping the official SDK's <code>McpClient</code> into AgentForge's <code>ToolExecutor</code> (<strong>P3 planned, not yet implemented</strong>).</li>
</ul>
<blockquote>
<p><strong>Conclusion</strong>: The target form adopts <strong>Option Three</strong>—the main path is delivered in-house first, and the official SDK serves as an optional bridge to be filled in later, both safeguarding the core constraints and not locking users into the in-house protocol stack.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="422-decision-basis">4.2.2 Decision Basis<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#422-decision-basis" class="hash-link" aria-label="Direct link to 4.2.2 Decision Basis" title="Direct link to 4.2.2 Decision Basis" translate="no">​</a></h3>
<table><thead><tr><th>Dimension</th><th>In-house</th><th>Official SDK</th></tr></thead><tbody><tr><td>JDK</td><td>8</td><td>17+</td></tr><tr><td>Dependencies</td><td>None</td><td>Jackson/Reactor/SLF4J</td></tr><tr><td>With <code>ToolExecutor</code></td><td>Direct implementation</td><td>Requires bridging</td></tr><tr><td>Spec follow-up</td><td>Follow it yourself</td><td>Official follows</td></tr><tr><td>Conformance tests</td><td>Do it yourself</td><td>Official does it</td></tr><tr><td><strong>AgentForge fit</strong></td><td><strong>High</strong></td><td>Low (requires isolation module)</td></tr></tbody></table>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="423-how-bridging-resolves-the-jdk-17-conflict">4.2.3 How Bridging Resolves the JDK 17 Conflict<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#423-how-bridging-resolves-the-jdk-17-conflict" class="hash-link" aria-label="Direct link to 4.2.3 How Bridging Resolves the JDK 17 Conflict" title="Direct link to 4.2.3 How Bridging Resolves the JDK 17 Conflict" translate="no">​</a></h3>
<p><strong>Question</strong>: The official SDK requires JDK 17, so doesn't setting up an "official bridge" module bring JDK 17 back again? Can JDK 8 still use MCP at all?</p>
<p><strong>First, let's clarify</strong>: MCP is a JSON-RPC protocol, <strong>independent of the JDK</strong>; JDK 17 is the bar for "the official Java SDK implementation", not the bar for MCP. JDK 8 can fully implement MCP—<code>ProcessBuilder</code> (stdio) + <code>HttpURLConnection</code> (HTTP) + the built-in <code>Json</code> is enough, no Jackson / Reactor needed.</p>
<p><strong>Key principle: one-way dependencies, upward bytecode compatibility.</strong> A module compiled with JDK 17 can depend on a module compiled with Java 8, but not the reverse. So let <strong>bridge depend on core</strong>, not core depend on bridge:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">agentforge-agent-core (release 8 / zero dependencies)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   ▲  defines ToolExecutor / McpClient / ToolSpecification</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   │  ← only depended upon, contains no MCP implementation</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">agentforge-mcp-sdk-bridge (release 17)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    depends on core + official SDK + Jackson / Reactor / SLF4J</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    wraps the official McpClient into core's ToolExecutor</span><br></div></code></pre></div></div>
<p>Thus JDK 17 types, Jackson, Reactor, and SLF4J are all locked inside the bridge, and <strong>will never appear on core's compile or runtime path</strong>.</p>
<br>
<p><strong>JDK Version × Implementation Path</strong></p>
<table><thead><tr><th>Runtime environment</th><th>In-house protocol stack (built into core)</th><th>Official SDK bridge (separate module)</th></tr></thead><tbody><tr><td>JDK 8</td><td>✅ The only choice</td><td>❌ Cannot be used (SDK requires 17)</td></tr><tr><td>JDK 17+</td><td>✅ Still usable (Java 8 bytecode is upward compatible, zero-dependency advantage retained)</td><td>✅ Optional, effective only when explicitly included</td></tr></tbody></table>
<blockquote>
<p><strong>Key point</strong>: The in-house stack is <strong>not JDK 8-exclusive</strong>; it also runs on JDK 17 / 21; the official bridge is "opt-in", <strong>not switched automatically by JDK version</strong>. The two paths are a <strong>deployment-time either/or choice</strong>, decided by the user.</p>
</blockquote>
<br>
<p><strong>Isolation Techniques</strong></p>
<ul>
<li class=""><strong>Build isolation</strong>: Multi-module project, core uses <code>maven.compiler.release=8</code>, bridge uses <code>17</code>; <strong>core's POM never declares bridge</strong>, so Jackson / Reactor / SLF4J cannot enter core's dependency tree.</li>
<li class=""><strong>Runtime isolation</strong>: The bridge is a separate artifact; JDK 8 users never load it unless they include it, avoiding <code>UnsupportedClassVersionError</code>.</li>
<li class=""><strong>Interface isolation</strong>: Core only exposes Java 8's <code>ToolExecutor</code> / <code>McpClient</code>, the bridge internally is compatible with official types, and only implements core interfaces upward.</li>
<li class=""><strong>Optional lazy loading</strong>: Use Java 8's built-in <code>java.util.ServiceLoader</code> to define the <code>McpClientFactory</code> SPI—if a bridge is present it is discovered, otherwise it falls back to the in-house <code>DefaultMcpClient</code>, keeping core zero-dependency throughout.</li>
</ul>
<blockquote>
<p><strong>Conclusion</strong>: Bridging is not "running the JDK 17 SDK on Java 8", but rather opening an <strong>optional, isolated</strong> door for users who "are willing to upgrade to JDK 17 and want official capabilities"; users who don't upgrade to JDK 17 go entirely with the in-house stack, and the two paths are each clean.</p>
</blockquote>
<blockquote>
<p><strong>Current progress</strong>: The in-house protocol stack is implemented; <code>agentforge-mcp-sdk-bridge</code> and the <code>McpClientFactory</code> SPI are both <strong>P3 planned, not yet delivered</strong>.</p>
</blockquote>
<br>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="43-overall-architecture-and-packaging">4.3 Overall Architecture and Packaging<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#43-overall-architecture-and-packaging" class="hash-link" aria-label="Direct link to 4.3 Overall Architecture and Packaging" title="Direct link to 4.3 Overall Architecture and Packaging" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="431-directory-structure">4.3.1 Directory Structure<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#431-directory-structure" class="hash-link" aria-label="Direct link to 4.3.1 Directory Structure" title="Direct link to 4.3.1 Directory Structure" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">agentforge-agent-core/src/main/java/cloud/changlu/agentforge/agent/tool/</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── mcp/</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── McpToolFactory.java         # Build entry: listTools → Map&lt;ToolSpecification, ToolExecutor&gt;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── McpToolExecutor.java        # implements ToolExecutor: tools/call</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── McpClient.java              # Client interface: serverAlias / listTools / callTool / close</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── DefaultMcpClient.java       # JSON-RPC lifecycle (initialize handshake, lazy initialization)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── McpProtocolException.java   # JSON-RPC error → exception</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── McpTransportException.java  # Transport failure exception</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── domain/</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   ├── McpTool.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   ├── McpCallToolResult.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   ├── McpContent.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   ├── McpServerInfo.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   └── McpCapabilities.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── transport/</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   ├── McpTransport.java       # SPI for send / request / close</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   ├── StdioMcpTransport.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   └── StreamableHttpMcpTransport.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   └── support/</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│       ├── JsonRpcCodec.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│       ├── McpJson.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│       ├── McpToolSpecificationMapper.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│       └── McpResultConverter.java</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="432-layered-responsibilities">4.3.2 Layered Responsibilities<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#432-layered-responsibilities" class="hash-link" aria-label="Direct link to 4.3.2 Layered Responsibilities" title="Direct link to 4.3.2 Layered Responsibilities" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">┌──────────────────────────────────────────────┐</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│ McpToolFactory / McpToolExecutor              │  Connects to Agent tool table</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├──────────────────────────────────────────────┤</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│ DefaultMcpClient (id increment, handshake, lazy init) │  Protocol layer</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├──────────────────────────────────────────────┤</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│ McpTransport (stdio / Streamable HTTP)        │  Transport layer</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">└──────────────────────────────────────────────┘</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="44-core-design-and-mapping">4.4 Core Design and Mapping<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#44-core-design-and-mapping" class="hash-link" aria-label="Direct link to 4.4 Core Design and Mapping" title="Direct link to 4.4 Core Design and Mapping" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="441-mcptoolexecutor-implements-toolexecutor">4.4.1 <code>McpToolExecutor implements ToolExecutor</code><a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#441-mcptoolexecutor-implements-toolexecutor" class="hash-link" aria-label="Direct link to 441-mcptoolexecutor-implements-toolexecutor" title="Direct link to 441-mcptoolexecutor-implements-toolexecutor" translate="no">​</a></h3>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">McpToolExecutor</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">implements</span><span class="token plain"> </span><span class="token class-name">ToolExecutor</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">McpClient</span><span class="token plain"> client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">// Bound at construction: which MCP Server this tool belongs to</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> remoteToolName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(98, 114, 164)">// Bound at construction: the original tool name on the Server (without alias prefix)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token class-name">McpToolExecutor</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">McpClient</span><span class="token plain"> client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> remoteToolName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">this</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">client </span><span class="token operator">=</span><span class="token plain"> client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">this</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">remoteToolName </span><span class="token operator">=</span><span class="token plain"> remoteToolName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)">/** Simplified interface: returns text only. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Override</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">execute</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ToolExecutionRequest</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">Object</span><span class="token plain"> memoryId</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">// Step ①: the arguments given by the model are a JSON string → restore to a Map</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">Object</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> args </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">McpJson</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">argumentsAsMap</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">arguments</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">// Step ②: use the bound original remote name to send tools/call, and flatten content into text</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token class-name">McpResultConverter</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">toText</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">callTool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">remoteToolName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> args</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)">/** Structured interface: additionally carries structuredContent and isError. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Override</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token class-name">ToolExecutionResult</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">executeWithResult</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ToolExecutionRequest</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">Object</span><span class="token plain"> memoryId</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">try</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token comment" style="color:rgb(98, 114, 164)">// Step ①: JSON string → Map (nested objects/arrays kept as-is)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">Object</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> args </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">McpJson</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">argumentsAsMap</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">arguments</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token comment" style="color:rgb(98, 114, 164)">// Step ②: determine "which Server to send to, which tool to call"—decided by the two fields bound at construction</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token class-name">McpCallToolResult</span><span class="token plain"> result </span><span class="token operator">=</span><span class="token plain"> client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">callTool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">remoteToolName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> args</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token comment" style="color:rgb(98, 114, 164)">// Step ③: lossless mapping back to AgentForge result</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token class-name">ToolExecutionResult</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">isError</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">result</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">isError</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">                    </span><span class="token comment" style="color:rgb(98, 114, 164)">// isError passed through as-is</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">result</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">result</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">structuredContent</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">           </span><span class="token comment" style="color:rgb(98, 114, 164)">// structuredContent → result</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">text</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">McpResultConverter</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">toText</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">result</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">      </span><span class="token comment" style="color:rgb(98, 114, 164)">// content text → text (fed back to model)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">McpProtocolException</span><span class="token plain"> e</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">                        </span><span class="token comment" style="color:rgb(98, 114, 164)">// JSON-RPC error (e.g. -32601)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token class-name">ToolExecutionResult</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">failure</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"MCP protocol error: "</span><span class="token plain"> </span><span class="token operator">+</span><span class="token plain"> e</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> e</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">McpTransportException</span><span class="token plain"> e</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">                       </span><span class="token comment" style="color:rgb(98, 114, 164)">// transport failure / timeout</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token class-name">ToolExecutionResult</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">failure</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"MCP transport error: "</span><span class="token plain"> </span><span class="token operator">+</span><span class="token plain"> e</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> e</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="442-mcptoolfactory-list--spec--executor">4.4.2 <code>McpToolFactory</code>: List → Spec + Executor<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#442-mcptoolfactory-list--spec--executor" class="hash-link" aria-label="Direct link to 442-mcptoolfactory-list--spec--executor" title="Direct link to 442-mcptoolfactory-list--spec--executor" translate="no">​</a></h3>
<p><strong>Core steps</strong>: ① Parse the alias (explicit takes priority, otherwise <code>client.serverAlias()</code>) → ② Fetch native tools (<code>tools/list</code>) → ③ White/blacklist filtering → ④ Concatenate alias to generate the model-visible name → ⑤ Bind <code>McpToolExecutor(client, original name)</code> → ⑥ Hand to <code>ToolService</code> to build the table by <code>spec.name()</code>.</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">McpToolFactory</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token class-name">McpToolFactory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)">/** Use the client's own serverAlias as the prefix. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ToolSpecification</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">ToolExecutor</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">buildTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">McpClient</span><span class="token plain"> client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">buildTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)">/** Explicit alias takes priority, falling back to client.serverAlias() when blank. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ToolSpecification</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">ToolExecutor</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">buildTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token class-name">McpClient</span><span class="token plain"> client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> serverAlias</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">buildTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> serverAlias</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)">/** When enabledTools is non-empty, only the remote tool names within it are exposed; those matched by disabledTools are hidden. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ToolSpecification</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">ToolExecutor</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">buildTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token class-name">McpClient</span><span class="token plain"> client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> serverAlias</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token class-name">Set</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> enabledTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">Set</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> disabledTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">// Step ①: determine the prefix—explicit alias takes priority, otherwise read client.serverAlias()</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">String</span><span class="token plain"> alias </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">serverAlias </span><span class="token operator">!=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token plain"> </span><span class="token operator">&amp;&amp;</span><span class="token plain"> </span><span class="token operator">!</span><span class="token plain">serverAlias</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">trim</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">isEmpty</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token operator">?</span><span class="token plain"> serverAlias </span><span class="token operator">:</span><span class="token plain"> client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">serverAlias</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ToolSpecification</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">ToolExecutor</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> result </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">LinkedHashMap</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">// Step ②: fetch the Server's native tools (the first call triggers initialize → initialized → tools/list)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">McpTool</span><span class="token plain"> tool </span><span class="token operator">:</span><span class="token plain"> client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">listTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token comment" style="color:rgb(98, 114, 164)">// Step ③: white/blacklist filtering (determined by "original tool name")</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">enabledTools </span><span class="token operator">!=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token plain"> </span><span class="token operator">&amp;&amp;</span><span class="token plain"> </span><span class="token operator">!</span><span class="token plain">enabledTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">isEmpty</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token operator">&amp;&amp;</span><span class="token plain"> </span><span class="token operator">!</span><span class="token plain">enabledTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">contains</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">continue</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">disabledTools </span><span class="token operator">!=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token plain"> </span><span class="token operator">&amp;&amp;</span><span class="token plain"> disabledTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">contains</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">continue</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token comment" style="color:rgb(98, 114, 164)">// Step ④: concatenate alias to generate the model-visible name; inputSchema passed through as-is; write ownership metadata</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token class-name">ToolSpecification</span><span class="token plain"> spec </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">ToolSpecification</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token function" style="color:rgb(80, 250, 123)">qualify</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">alias</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">          </span><span class="token comment" style="color:rgb(98, 114, 164)">// fs__read_file</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">parameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">McpToolSpecificationMapper</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">toToolParameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">inputSchema</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">addMetadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"mcp.server"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> alias </span><span class="token operator">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token plain"> </span><span class="token operator">?</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">""</span><span class="token plain"> </span><span class="token operator">:</span><span class="token plain"> alias</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">addMetadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"mcp.tool"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token comment" style="color:rgb(98, 114, 164)">// Step ⑤: bind (client + original remote name)—used at runtime to route back to the corresponding Server</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            result</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">put</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">spec</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">McpToolExecutor</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">// Step ⑥: return the spec→executor mapping, handed to ToolService.tools(...) to build the table by spec.name()</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> result</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">qualify</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">String</span><span class="token plain"> alias</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> toolName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">alias </span><span class="token operator">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token plain"> </span><span class="token operator">||</span><span class="token plain"> alias</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">trim</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">isEmpty</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">?</span><span class="token plain"> toolName </span><span class="token operator">:</span><span class="token plain"> alias </span><span class="token operator">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"__"</span><span class="token plain"> </span><span class="token operator">+</span><span class="token plain"> toolName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="443-schema-mapping-zero-loss">4.4.3 Schema Mapping (Zero Loss)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#443-schema-mapping-zero-loss" class="hash-link" aria-label="Direct link to 4.4.3 Schema Mapping (Zero Loss)" title="Direct link to 4.4.3 Schema Mapping (Zero Loss)" translate="no">​</a></h3>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token class-name">ToolParameters</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">toToolParameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">Object</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> inputSchema</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">inputSchema </span><span class="token operator">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token plain"> </span><span class="token operator">||</span><span class="token plain"> inputSchema</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">isEmpty</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token class-name">ToolParameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">empty</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">      </span><span class="token comment" style="color:rgb(98, 114, 164)">// {"type":"object","properties":{}}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token class-name">ToolParameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">inputSchema</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">// pass through JSON Schema as-is</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="444-error-mapping">4.4.4 Error Mapping<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#444-error-mapping" class="hash-link" aria-label="Direct link to 4.4.4 Error Mapping" title="Direct link to 4.4.4 Error Mapping" translate="no">​</a></h3>
<table><thead><tr><th>MCP</th><th>AgentForge</th></tr></thead><tbody><tr><td><code>result.isError = true</code></td><td><code>ToolExecutionResult.isError(true)</code> + text returned to the model for self-correction</td></tr><tr><td>JSON-RPC <code>error</code> (<code>-32601/-32602</code>…)</td><td><code>McpProtocolException</code> → failure result / error handler</td></tr><tr><td><code>-32022</code> version unsupported</td><td>Throws <code>McpProtocolException</code> (auto-reselect version is P1)</td></tr><tr><td>Transport exception / timeout</td><td><code>McpTransportException</code> → failure result + alert</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-agentforge-hands-on-code">5. AgentForge Hands-On Code<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#5-agentforge-hands-on-code" class="hash-link" aria-label="Direct link to 5. AgentForge Hands-On Code" title="Direct link to 5. AgentForge Hands-On Code" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="51-minimal-integration-two-transports">5.1 Minimal Integration: Two Transports<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#51-minimal-integration-two-transports" class="hash-link" aria-label="Direct link to 5.1 Minimal Integration: Two Transports" title="Direct link to 5.1 Minimal Integration: Two Transports" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="511-integrating-a-local-stdio-server">5.1.1 Integrating a Local stdio Server<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#511-integrating-a-local-stdio-server" class="hash-link" aria-label="Direct link to 5.1.1 Integrating a Local stdio Server" title="Direct link to 5.1.1 Integrating a Local stdio Server" translate="no">​</a></h3>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// 1) Start a stdio MCP Server (local child process, newline-framed JSON-RPC)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">McpClient</span><span class="token plain"> fs </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">DefaultMcpClient</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">serverAlias</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"fs"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">transport</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">StdioMcpTransport</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">command</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"npx"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"-y"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"@modelcontextprotocol/server-filesystem"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/tmp"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// 2) Fetch tools and register into ToolService (reads the client's serverAlias, auto-prefixes: fs__read_file)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ToolService</span><span class="token plain"> toolService </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">ToolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">McpToolFactory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">buildTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">fs</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// 3) Coexist with local @Tool (same tool table)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">WeatherTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// 4) Assemble the ReActAgent</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ReActAgent</span><span class="token plain"> agent </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">ReActAgent</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">chatModel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">chatModel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: <code>DefaultMcpClient</code> is <strong>lazy</strong>—the <code>initialize</code> handshake and <code>notifications/initialized</code> are sent only on the first <code>listTools()</code> / <code>callTool()</code>, and <code>tools/list</code> is sent afterward. <code>StdioMcpTransport.of("npx", "-y", ...)</code> is an equivalent shorthand.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="512-integrating-a-remote-streamable-http-server">5.1.2 Integrating a Remote Streamable HTTP Server<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#512-integrating-a-remote-streamable-http-server" class="hash-link" aria-label="Direct link to 5.1.2 Integrating a Remote Streamable HTTP Server" title="Direct link to 5.1.2 Integrating a Remote Streamable HTTP Server" translate="no">​</a></h3>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">McpClient</span><span class="token plain"> remote </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">DefaultMcpClient</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">serverAlias</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"github"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">transport</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">StreamableHttpMcpTransport</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">endpoint</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"https://mcp.example.com/mcp"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">header</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Authorization"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Bearer "</span><span class="token plain"> </span><span class="token operator">+</span><span class="token plain"> token</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">// custom headers passed through with the request</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">McpToolFactory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">buildTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">remote</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">// reads the client's serverAlias, tool names look like github__search_repos</span><br></div></code></pre></div></div>
<p><strong>The difference from stdio is only at the transport layer</strong>: <code>serverAlias="github"</code> → model-visible name <code>github__search_repos</code>; the positioning and parameter conversion of <code>buildTools</code> / <code>ToolService</code> / <code>ToolExecutor</code> are <strong>completely identical</strong>. On the HTTP side, each <code>tools/call</code> is a POST:</p>
<ul>
<li class="">Request body = the aforementioned JSON-RPC (<code>{"jsonrpc","id","method":"tools/call","params":{...}}</code>);</li>
<li class="">After the handshake, <code>MCP-Protocol-Version</code> is automatically included, and custom headers (<code>Authorization</code>) are passed through as-is;</li>
<li class="">The response is extracted by <code>id</code> (<code>application/json</code> or SSE <code>data:</code>), then goes through the same <code>content</code> / <code>isError</code> mapping.</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="513-on-demand-filtering-and-closing">5.1.3 On-Demand Filtering and Closing<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#513-on-demand-filtering-and-closing" class="hash-link" aria-label="Direct link to 5.1.3 On-Demand Filtering and Closing" title="Direct link to 5.1.3 On-Demand Filtering and Closing" translate="no">​</a></h3>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// Only expose read_file, hide the rest of the tools (empty/null whitelist means all)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ToolSpecification</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">ToolExecutor</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> tools </span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">McpToolFactory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">buildTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"fs"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token class-name">Collections</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">singleton</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"read_file"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">// whitelist</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">                              </span><span class="token comment" style="color:rgb(98, 114, 164)">// blacklist</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">close</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">// stdio: close and reclaim the child process; HTTP: release the transport</span><br></div></code></pre></div></div>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="514-underlying-core-principle-how-a-single-query-is-closed-loop-real-function-call-sequence">5.1.4 Underlying Core Principle: How a Single Query Is Closed Loop (Real function call Sequence)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#514-underlying-core-principle-how-a-single-query-is-closed-loop-real-function-call-sequence" class="hash-link" aria-label="Direct link to 5.1.4 Underlying Core Principle: How a Single Query Is Closed Loop (Real function call Sequence)" title="Direct link to 5.1.4 Underlying Core Principle: How a Single Query Is Closed Loop (Real function call Sequence)" translate="no">​</a></h3>
<p>Scenario: MCP <code>fs</code> (filesystem) and MCP <code>weather</code> are registered at the same time, and the user asks "read out the contents of /tmp/a.txt, and check the weather in Hangzhou". The following unfolds <strong>in the order actually occurring</strong>.</p>
<p><strong>① The MCP Server first returns the native <code>tools/list</code> (without prefix)</strong></p>
<p>AgentForge first calls <code>tools/list</code> on each Server and gets the <strong>tool's own name</strong> (at this point there is no <code>fs__</code> prefix yet):</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// ← fs server's tools/list response</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"result"</span><span class="token operator">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"tools"</span><span class="token operator">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"read_file"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"description"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"Read a file"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"inputSchema"</span><span class="token operator">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"object"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"properties"</span><span class="token operator">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"path"</span><span class="token operator">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"string"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"required"</span><span class="token operator">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"path"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// ← weather server's tools/list response</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token number">2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"result"</span><span class="token operator">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"tools"</span><span class="token operator">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"description"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"Query weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"inputSchema"</span><span class="token operator">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"object"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"properties"</span><span class="token operator">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"location"</span><span class="token operator">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"string"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"required"</span><span class="token operator">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"location"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p><strong>② The middleware assembles the alias (<code>McpToolFactory</code>)</strong></p>
<p><code>buildTools(client)</code> transforms the above <strong>original tools</strong>: <code>alias__original name</code>, and at the same time binds the executor and writes it into <code>ToolService</code>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">fs      + read_file    → spec.name = "fs__read_file"        + McpToolExecutor(fsClient,      "read_file")</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">weather + get_weather  → spec.name = "weather__get_weather" + McpToolExecutor(weatherClient, "get_weather")</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Note</strong>: <strong>The original name is always retained in the executor</strong> (<code>read_file</code> / <code>get_weather</code>), and the prefix is only added to the model-visible name—this is the basis for the subsequent "strip prefix, send original name".</p>
</blockquote>
<p><strong>③ The <code>tools[]</code> seen by the large model (alias form)</strong></p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"function"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"function"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"fs__read_file"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"parameters"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"object"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"properties"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"path"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"string"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"required"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"path"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"function"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"function"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"weather__get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"parameters"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"object"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"properties"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"location"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"string"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"required"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"location"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><br></div></code></pre></div></div>
<p><strong>④ The combined function call returned by the model (OpenAI form, two at a time)</strong></p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"tool_calls"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"call_a1b2"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"function"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"function"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"fs__read_file"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"arguments"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"{\"path\":\"/tmp/a.txt\"}"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"call_c3d4"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"function"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"function"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"weather__get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"arguments"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"{\"location\":\"杭州\"}"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key</strong>: <code>arguments</code> is an <strong>escaped JSON string</strong>, not an object (for Anthropic it is the <code>tool_use.input</code> object). AgentForge normalizes both into <code>ToolExecutionRequest(id, name, arguments)</code>—<code>arguments</code> is <strong>always a string</strong>.</p>
</blockquote>
<p><strong>⑤ Normalization → ToolService locates the executor by alias</strong></p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">ToolExecutionRequest{id="call_a1b2", name="fs__read_file",       arguments="{\"path\":\"/tmp/a.txt\"}"}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">ToolExecutionRequest{id="call_c3d4", name="weather__get_weather", arguments="{\"location\":\"杭州\"}"}</span><br></div></code></pre></div></div>
<ul>
<li class=""><code>ToolService</code> uses <code>name</code> to hit <code>toolExecutors</code>: <code>fs__read_file</code> → the <code>McpToolExecutor</code> bound to <code>fsClient</code> + <code>read_file</code>; <code>weather__get_weather</code> → <code>weatherClient</code> + <code>get_weather</code>.</li>
</ul>
<p><strong>⑥ Strip the prefix + restore parameters → send the original <code>tools/call</code></strong></p>
<ul>
<li class=""><code>McpJson.argumentsAsMap(arguments)</code> <strong>restores the string to an object</strong>;</li>
<li class="">Use the <strong>original remote name</strong> in the executor (strip the <code>fs__</code> / <code>weather__</code> prefix) to send:</li>
</ul>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// → fs server</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"method"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"tools/call"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"params"</span><span class="token operator">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"read_file"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"arguments"</span><span class="token operator">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"path"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"/tmp/a.txt"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// → weather server</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"method"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"tools/call"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token property">"params"</span><span class="token operator">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"arguments"</span><span class="token operator">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"location"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"杭州"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p><strong>⑦ Response → fed back to the model → final answer</strong></p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"result"</span><span class="token operator">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"content"</span><span class="token operator">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"text"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"text"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"hello"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"isError"</span><span class="token operator">:</span><span class="token boolean">false</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"result"</span><span class="token operator">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"content"</span><span class="token operator">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"text"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"text"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"杭州 26℃，晴"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"isError"</span><span class="token operator">:</span><span class="token boolean">false</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tool"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"tool_call_id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"call_a1b2"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"hello"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tool"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"tool_call_id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"call_c3d4"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"杭州 26℃，晴"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><br></div></code></pre></div></div>
<p>The final model gives: "The content of /tmp/a.txt is hello; Hangzhou is currently 26°C and sunny."</p>
<blockquote>
<p><strong>Complete chain</strong>: MCP native tool name (<code>read_file</code>) → middleware concatenates alias (<code>fs__read_file</code>) → the model sees and returns the alias → <code>ToolService</code> locates the executor by alias → strip prefix and restore the original name → send to the corresponding Server's <code>tools/call</code>.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="52-transport-implementation-and-protocol-versions">5.2 Transport Implementation and Protocol Versions<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#52-transport-implementation-and-protocol-versions" class="hash-link" aria-label="Direct link to 5.2 Transport Implementation and Protocol Versions" title="Direct link to 5.2 Transport Implementation and Protocol Versions" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="521-stdio-newline-framing">5.2.1 stdio (Newline Framing)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#521-stdio-newline-framing" class="hash-link" aria-label="Direct link to 5.2.1 stdio (Newline Framing)" title="Direct link to 5.2.1 stdio (Newline Framing)" translate="no">​</a></h3>
<ul>
<li class=""><code>ProcessBuilder</code> spawns a child process, <code>stdin</code> / <code>stdout</code> framed by <strong>newlines in UTF-8</strong>: each JSON-RPC message on one line, with embedded newlines removed before sending;</li>
<li class=""><code>stderr</code> is continuously drained to the log by a background daemon thread, to avoid the pipe filling up and blocking;</li>
<li class="">Closing: close <code>stdin</code> → <code>destroy()</code> → wait 2s → <code>destroyForcibly()</code>.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="522-streamable-http-reusing-the-built-in-httptransport">5.2.2 Streamable HTTP (Reusing the Built-in <code>HttpTransport</code>)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#522-streamable-http-reusing-the-built-in-httptransport" class="hash-link" aria-label="Direct link to 522-streamable-http-reusing-the-built-in-httptransport" title="Direct link to 522-streamable-http-reusing-the-built-in-httptransport" translate="no">​</a></h3>
<ul>
<li class="">Single endpoint, POST only; reuses <code>JdkHttpTransport</code> and the built-in <code>Json</code>;</li>
<li class="">Request headers: <code>Content-Type: application/json</code>, <code>Accept: application/json, text/event-stream</code>; after the handshake, <code>MCP-Protocol-Version</code> is appended, and other custom headers (such as <code>Authorization</code>) are passed through as-is;</li>
<li class="">Response: <code>application/json</code> (a single JSON-RPC message) or request-level <code>text/event-stream</code> (parse <code>data:</code> events, select the matching response by <code>id</code>);</li>
<li class=""><strong>P0 boundary</strong>: <code>HttpResponse</code> does not expose response headers, so <code>Mcp-Session-Id</code> is not returned for now (usable for stateless Servers; stateful sessions are P1).</li>
</ul>
<blockquote>
<p><strong>Note</strong>: <code>JdkHttpTransport</code> is based on <code>HttpURLConnection</code> and does not support <code>PATCH</code>; MCP itself does not use PATCH, so there is no impact.</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="523-current-state-of-protocol-version-support">5.2.3 Current State of Protocol Version Support<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#523-current-state-of-protocol-version-support" class="hash-link" aria-label="Direct link to 5.2.3 Current State of Protocol Version Support" title="Direct link to 5.2.3 Current State of Protocol Version Support" translate="no">​</a></h3>
<ul>
<li class=""><strong>Implemented (P0)</strong>: Legacy <code>initialize</code> handshake (default version <code>2025-06-18</code>, can be overridden with <code>protocolVersion(...)</code>), sending <code>notifications/initialized</code> after the handshake;</li>
<li class=""><strong>Not implemented (P1)</strong>: Modern / <code>server/discover</code> auto-detection. The "dual-era detection" described in 4.2.3 is the target design; currently it works along the legacy path.</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="53-official-sdk-bridge-module-p3-planned-not-yet-implemented">5.3 Official SDK Bridge Module (P3 Planned, Not Yet Implemented)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#53-official-sdk-bridge-module-p3-planned-not-yet-implemented" class="hash-link" aria-label="Direct link to 5.3 Official SDK Bridge Module (P3 Planned, Not Yet Implemented)" title="Direct link to 5.3 Official SDK Bridge Module (P3 Planned, Not Yet Implemented)" translate="no">​</a></h2>
<p><strong>Status</strong>: <code>agentforge-mcp-sdk-bridge</code> is <strong>not yet implemented</strong>, and belongs to roadmap P3; its dependency isolation strategy is described in 4.2.3. After delivery, the exact Maven coordinates, assembly API, and configuration examples will be added, to avoid documentation not matching the code.</p>
<p><strong>Target form</strong> (for alignment during implementation):</p>
<ul>
<li class="">A separate JDK 17 artifact, with one-way <code>bridge → core</code> dependency, still outputting <code>Map&lt;ToolSpecification, ToolExecutor&gt;</code>;</li>
<li class="">The business side only replaces <code>McpToolFactory</code> with the official bridge factory, and everything else (<code>ToolService</code> / <code>ReActAgent</code>) stays unchanged;</li>
<li class="">The <strong>same Server should not</strong> be registered with both the bridge and the non-bridge stack at the same time, to avoid tool name conflicts.</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-verification-testing-and-support-level">6. Verification Testing and Support Level<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#6-verification-testing-and-support-level" class="hash-link" aria-label="Direct link to 6. Verification Testing and Support Level" title="Direct link to 6. Verification Testing and Support Level" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="61-verification-and-testing">6.1 Verification and Testing<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#61-verification-and-testing" class="hash-link" aria-label="Direct link to 6.1 Verification and Testing" title="Direct link to 6.1 Verification and Testing" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="611-quick-verification-stdio-real-public-server">6.1.1 Quick Verification: stdio (Real Public Server)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#611-quick-verification-stdio-real-public-server" class="hash-link" aria-label="Direct link to 6.1.1 Quick Verification: stdio (Real Public Server)" title="Direct link to 6.1.1 Quick Verification: stdio (Real Public Server)" translate="no">​</a></h3>
<p>The least hassle self-check—use the official filesystem Server to start a local child process, and run through "discovery → call → feed back":</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">McpStdioQuickTest</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Test</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">shouldListAndCallPublicFilesystemServer</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">throws</span><span class="token plain"> </span><span class="token class-name">Exception</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">Path</span><span class="token plain"> dir </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">Files</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">createTempDirectory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"mcp-demo"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">Path</span><span class="token plain"> file </span><span class="token operator">=</span><span class="token plain"> dir</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">resolve</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"a.txt"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">McpClient</span><span class="token plain"> client </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">DefaultMcpClient</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">serverAlias</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"fs"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">transport</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">StdioMcpTransport</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">of</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token string" style="color:rgb(255, 121, 198)">"npx"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"-y"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"@modelcontextprotocol/server-filesystem"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> dir</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">toString</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">try</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token class-name">ToolService</span><span class="token plain"> toolService </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">ToolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">McpToolFactory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">buildTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"fs"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token function" style="color:rgb(80, 250, 123)">assertNotNull</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">toolExecutors</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"fs__write_file"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">toolExecutors</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"fs__write_file"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">execute</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token class-name">ToolExecutionRequest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"fs__write_file"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                            </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">arguments</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"{\"path\":"</span><span class="token plain"> </span><span class="token operator">+</span><span class="token plain"> </span><span class="token class-name">Json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">stringify</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">file</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">toString</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                    </span><span class="token operator">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">",\"content\":\"hello mcp\"}"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                            </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token class-name">String</span><span class="token plain"> text </span><span class="token operator">=</span><span class="token plain"> toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">toolExecutors</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"fs__read_file"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">execute</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token class-name">ToolExecutionRequest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"fs__read_file"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                            </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">arguments</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"{\"path\":"</span><span class="token plain"> </span><span class="token operator">+</span><span class="token plain"> </span><span class="token class-name">Json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">stringify</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">file</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">toString</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"}"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                            </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token function" style="color:rgb(80, 250, 123)">assertTrue</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">contains</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"hello mcp"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">finally</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">close</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p>This case has been delivered as a runnable test: <code>agentforge-agent-core/src/test/java/cloud/changlu/agentforge/agent/tool/mcp/real/McpRealStudioTest.java</code> (a real MCP Server + <code>ScriptedChatModel</code> driving the complete ReAct loop, runs by default, can be disabled with <code>-Dagentforge.mcp.live=false</code>).</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="612-quick-verification-http-real-public-server">6.1.2 Quick Verification: HTTP (Real Public Server)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#612-quick-verification-http-real-public-server" class="hash-link" aria-label="Direct link to 6.1.2 Quick Verification: HTTP (Real Public Server)" title="Direct link to 6.1.2 Quick Verification: HTTP (Real Public Server)" translate="no">​</a></h3>
<p>Switch to the Streamable HTTP transport and the rest of the code is completely identical (the only difference is <code>transport</code>):</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">McpHttpQuickTest</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Test</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">shouldListPublicHttpServerTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">McpClient</span><span class="token plain"> client </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">DefaultMcpClient</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">serverAlias</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"wiki"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">transport</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">StreamableHttpMcpTransport</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">endpoint</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"https://mcp.deepwiki.com/mcp"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">try</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ToolSpecification</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">ToolExecutor</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> tools </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">McpToolFactory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">buildTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"wiki"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token function" style="color:rgb(80, 250, 123)">assertFalse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">isEmpty</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">// deepwiki actually exposes 3 tools</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">finally</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">close</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>In one sentence</strong>: stdio and HTTP <strong>differ only by one <code>transport</code></strong>, and the usage of <code>McpToolFactory</code> / <code>ToolService</code> / <code>ReActAgent</code> is exactly the same.</p>
</blockquote>
<blockquote>
<p>This case has been delivered as a runnable test: <code>agentforge-agent-core/src/test/java/cloud/changlu/agentforge/agent/tool/mcp/real/McpRealHttpTest.java</code> (real DeepWiki public service, discovery + real call to <code>read_wiki_structure</code>).</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="613-measured-list-of-public-mcp-services">6.1.3 Measured List of Public MCP Services<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#613-measured-list-of-public-mcp-services" class="hash-link" aria-label="Direct link to 6.1.3 Measured List of Public MCP Services" title="Direct link to 6.1.3 Measured List of Public MCP Services" translate="no">​</a></h3>
<p>Measurement time: 2026-10-05, all are authentication-free public services:</p>
<table><thead><tr><th>Form</th><th>Service</th><th>Start / Address</th><th>Measured tool count</th><th>Result</th></tr></thead><tbody><tr><td>stdio</td><td>filesystem</td><td><code>npx -y @modelcontextprotocol/server-filesystem &lt;dir&gt;</code></td><td>14</td><td>✅</td></tr><tr><td>stdio</td><td>memory</td><td><code>npx -y @modelcontextprotocol/server-memory</code></td><td>9</td><td>✅</td></tr><tr><td>stdio</td><td>sequential-thinking</td><td><code>npx -y @modelcontextprotocol/server-sequential-thinking</code></td><td>1</td><td>✅</td></tr><tr><td>stdio</td><td>everything</td><td><code>npx -y @modelcontextprotocol/server-everything</code></td><td>13</td><td>✅</td></tr><tr><td>HTTP</td><td>deepwiki</td><td><code>https://mcp.deepwiki.com/mcp</code></td><td>3</td><td>✅</td></tr><tr><td>HTTP</td><td>context7</td><td><code>https://mcp.context7.com/mcp</code></td><td>2</td><td>✅</td></tr><tr><td>HTTP</td><td>gitmcp</td><td><code>https://gitmcp.io/{owner}/{repo}</code></td><td>—</td><td>⚠️ Requires <code>Mcp-Session-Id</code> (P1)</td></tr></tbody></table>
<ul>
<li class="">Typical tool names: filesystem → <code>read_file</code> / <code>write_file</code> / <code>list_directory</code>…; everything → <code>echo</code> / <code>get-sum</code> / <code>get-env</code>…; deepwiki → <code>ask_wiki_question</code> / <code>read_wiki_structure</code> / <code>read_wiki_contents</code>; context7 → <code>resolve-library-id</code> / <code>query-docs</code>.</li>
<li class="">Reproduction commands:</li>
</ul>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># Public service scan (requires explicitly enabling live)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">mvn </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">test</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-Dagentforge.mcp.live</span><span class="token operator">=</span><span class="token plain">true </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-Dtest</span><span class="token operator">=</span><span class="token plain">McpPublicServersLiveTest,McpStdioLiveTest,McpHttpLiveTest</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># Real application tests of documentation cases (run by default, disable with -Dagentforge.mcp.live=false)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">mvn </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">test</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-Dtest</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">'cloud.changlu.agentforge.agent.tool.mcp.real.**'</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Note</strong>: After <code>initialize</code>, <code>gitmcp</code> requires subsequent requests to return <code>Mcp-Session-Id</code>, and the current P0 stateless implementation reports <code>Bad Request: Mcp-Session-Id header is required</code>—this is exactly why stateful HTTP sessions are left to P1.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="614-offline-unit-tests-no-real-server-needed">6.1.4 Offline Unit Tests (No Real Server Needed)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#614-offline-unit-tests-no-real-server-needed" class="hash-link" aria-label="Direct link to 6.1.4 Offline Unit Tests (No Real Server Needed)" title="Direct link to 6.1.4 Offline Unit Tests (No Real Server Needed)" translate="no">​</a></h3>
<p>Mock <code>McpClient</code> (<code>FakeMcpClient</code>) and inject fixed tools and results to run offline:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">FakeMcpClient</span><span class="token plain"> client </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">FakeMcpClient</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"demo"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">Collections</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">singletonList</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">McpTool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"查询天气"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">weatherSchema</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ToolSpecification</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">ToolExecutor</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> tools </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">McpToolFactory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">buildTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"demo"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">assertEquals</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"demo__get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">keySet</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">iterator</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">next</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>Covered: <code>JsonRpcCodec</code> encoding/decoding, <code>tools/list</code> mapping, name disambiguation, white/blacklist filtering, <code>tools/call</code> parameter pass-through, <code>isError</code> routing, protocol / transport error failure-ization, handshake timing, both HTTP JSON and SSE response forms; plus an end-to-end case with a real <code>JdkHttpTransport</code> + local HTTP Server.</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="615-risks-and-conformance">6.1.5 Risks and Conformance<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#615-risks-and-conformance" class="hash-link" aria-label="Direct link to 6.1.5 Risks and Conformance" title="Direct link to 6.1.5 Risks and Conformance" translate="no">​</a></h3>
<ul>
<li class=""><strong>Stateful HTTP</strong>: requires returning <code>Mcp-Session-Id</code> (gitmcp's measured limitation is exactly this), to be filled in at P1.</li>
<li class=""><strong>Spec follow-up</strong>: In-house needs to continuously follow the revisions and deprecations of <code>2024-11-05 → 2026-07-28</code>; self-test the message structure against the official <strong>MCP Inspector</strong> / <strong>conformance</strong>.</li>
<li class=""><strong>Dual-era complexity</strong>: <code>initialize</code> coexists with per-request <code>_meta</code>, and detection and caching must be done correctly (P1).</li>
<li class=""><strong>HTTP streaming</strong>: parsing and cancellation of request-level SSE (cancellation by closing the stream) must be implemented correctly.</li>
<li class=""><strong>Security</strong>: Tool Poisoning, approval (human-in-the-loop), and credential management are indispensable.</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="62-support-level-tiers-and-roadmap">6.2 Support Level Tiers and Roadmap<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#62-support-level-tiers-and-roadmap" class="hash-link" aria-label="Direct link to 6.2 Support Level Tiers and Roadmap" title="Direct link to 6.2 Support Level Tiers and Roadmap" translate="no">​</a></h2>
<table><thead><tr><th>Stage</th><th>Capability</th><th>Transport</th><th>Description</th></tr></thead><tbody><tr><td><strong>P0</strong></td><td><code>tools/list</code> + <code>tools/call</code></td><td>stdio / Streamable HTTP</td><td>Minimum viable: discovery + execution</td></tr><tr><td><strong>P0</strong></td><td>Name disambiguation / tool filtering / metadata</td><td>—</td><td><code>serverAlias__</code> prefix, whitelist</td></tr><tr><td><strong>P0</strong></td><td>Error routing (isError / protocol error)</td><td>—</td><td>Return to model for self-correction</td></tr><tr><td><strong>P1</strong></td><td>Dual-era compatibility + timeout/cancellation + close/restart</td><td>stdio / HTTP</td><td>Stability</td></tr><tr><td><strong>P1</strong></td><td><code>notifications/tools/list_changed</code> hot update</td><td>stdio / HTTP</td><td>Tool table refresh</td></tr><tr><td><strong>P1</strong></td><td>Resources / Prompts (read)</td><td>stdio / HTTP</td><td><code>resources/read</code>, <code>prompts/get</code></td></tr><tr><td><strong>P2</strong></td><td>MRTR (<code>InputRequiredResult</code>)</td><td>stdio / HTTP</td><td>Interactive input</td></tr><tr><td><strong>P2</strong></td><td><code>subscriptions/listen</code></td><td>HTTP</td><td>Change subscription</td></tr><tr><td><strong>P2</strong></td><td>OAuth 2.1 / OIDC</td><td>HTTP</td><td>Remote authentication</td></tr><tr><td><strong>P2</strong></td><td><code>x-mcp-header</code> mirroring</td><td>HTTP</td><td>Gateway routing</td></tr><tr><td><strong>P3</strong></td><td>Server side (exposing AgentForge <code>@Tool</code> as MCP)</td><td>stdio / HTTP</td><td>Reverse empowerment</td></tr><tr><td><strong>P3</strong></td><td>Official SDK bridge module (JDK17)</td><td>All</td><td>Optional conformance solution</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="621-consistency-with-http--local-modes">6.2.1 Consistency with <code>http</code> / <code>local</code> Modes<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#621-consistency-with-http--local-modes" class="hash-link" aria-label="Direct link to 621-consistency-with-http--local-modes" title="Direct link to 621-consistency-with-http--local-modes" translate="no">​</a></h3>
<table><thead><tr><th>Dimension</th><th><code>local</code></th><th><code>http</code></th><th><code>mcp</code> (this article)</th></tr></thead><tbody><tr><td>Factory</td><td><code>LocalToolFactory</code></td><td><code>HttpToolFactory</code></td><td><code>McpToolFactory</code></td></tr><tr><td>Executor</td><td><code>LocalToolExecutor</code></td><td><code>HttpToolExecutor</code></td><td><code>McpToolExecutor</code></td></tr><tr><td>Discovery</td><td>Scan <code>@Tool</code></td><td>Read configuration</td><td><code>tools/list</code> dynamic</td></tr><tr><td>Schema</td><td>Reflection-generated</td><td>Configuration-generated</td><td>Delivered by Server</td></tr><tr><td>Error</td><td><code>ToolExecutionResult</code></td><td>Text "HTTP request failed"</td><td><code>isError</code> lossless mapping</td></tr><tr><td>Subpackage</td><td><code>support/</code></td><td><code>domain/enums/support/parser/</code></td><td><code>domain/transport/support/</code></td></tr></tbody></table>
<blockquote>
<p><strong>Key point</strong>: The MCP mode is orthogonal to the two existing modes and can be registered into the same <code>ToolService</code> at the same time, so the model sees a unified tool list.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-summary-and-outlook">7. Summary and Outlook<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#7-summary-and-outlook" class="hash-link" aria-label="Direct link to 7. Summary and Outlook" title="Direct link to 7. Summary and Outlook" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="71-summary-and-outlook">7.1 Summary and Outlook<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#71-summary-and-outlook" class="hash-link" aria-label="Direct link to 7.1 Summary and Outlook" title="Direct link to 7.1 Summary and Outlook" translate="no">​</a></h2>
<ol>
<li class=""><strong>Research conclusion</strong>: All three support stdio / HTTP; <strong>LangChain4j is in-house, Spring AI and AgentScope are based on the official MCP Java SDK</strong>; the official SDK is the de facto standard but requires JDK 17+.</li>
<li class=""><strong>AgentForge's choice</strong>: <strong>In-house core protocol stack (Java 8 / zero dependencies, delivered) + optional official SDK bridge (P3 planned)</strong>.</li>
<li class=""><strong>Integration point</strong>: <code>McpToolExecutor implements ToolExecutor</code> + <code>McpToolFactory</code>, seamlessly merged into <code>ToolService</code> and the ReAct loop.</li>
<li class=""><strong>Support cadence</strong>: P0 connects stdio / Streamable HTTP's <code>tools/list</code> + <code>tools/call</code>; P1 adds hot update and resource prompts; P2 adds MRTR / subscriptions / OAuth; P3 reverse Server and official bridge.</li>
<li class=""><strong>Outlook</strong>: As the spec evolves, fill in MRTR, <code>subscriptions/listen</code>, and OAuth hardening, and explore reverse-exposing AgentForge's <code>@Tool</code> as an MCP Server.</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<p>[1]. <a href="https://modelcontextprotocol.io/specification/2026-07-28" target="_blank" rel="noopener noreferrer" class="">MCP Official Specification (2026-07-28)</a></p>
<p>[2]. <a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/lifecycle" target="_blank" rel="noopener noreferrer" class="">MCP Versioning and Compatibility</a></p>
<p>[3]. <a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http" target="_blank" rel="noopener noreferrer" class="">MCP Streamable HTTP Transport</a></p>
<p>[4]. <a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/stdio" target="_blank" rel="noopener noreferrer" class="">MCP stdio Transport</a></p>
<p>[5]. <a href="https://modelcontextprotocol.io/specification/2026-07-28/server/tools" target="_blank" rel="noopener noreferrer" class="">MCP Tools</a></p>
<p>[6]. <a href="https://java.sdk.modelcontextprotocol.io/latest/" target="_blank" rel="noopener noreferrer" class="">Official MCP Java SDK Documentation</a></p>
<p>[7]. <a href="https://github.com/modelcontextprotocol/java-sdk" target="_blank" rel="noopener noreferrer" class="">MCP Java SDK (GitHub)</a></p>
<p>[8]. <a href="https://docs.langchain4j.dev/tutorials/mcp/" target="_blank" rel="noopener noreferrer" class="">LangChain4j MCP Documentation</a></p>
<p>[9]. <a href="https://github.com/langchain4j/langchain4j/tree/main/langchain4j-mcp" target="_blank" rel="noopener noreferrer" class="">langchain4j-mcp Module (GitHub)</a></p>
<p>[10]. <a href="https://docs.spring.io/spring-ai/reference/api/mcp/mcp-overview.html" target="_blank" rel="noopener noreferrer" class="">Spring AI MCP Overview</a></p>
<p>[11]. <a href="https://docs.spring.io/spring-ai/reference/api/mcp/mcp-client-boot-starter-docs.html" target="_blank" rel="noopener noreferrer" class="">Spring AI MCP Client Boot Starters</a></p>
<p>[12]. <a href="https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html" target="_blank" rel="noopener noreferrer" class="">Spring AI MCP Server Boot Starters</a></p>
<p>[13]. <a href="https://java.agentscope.io/v1/en/docs/task/mcp" target="_blank" rel="noopener noreferrer" class="">AgentScope Java MCP Documentation</a></p>
<p>[14]. <a href="https://github.com/agentscope-ai/agentscope-java" target="_blank" rel="noopener noreferrer" class="">AgentScope Java (GitHub)</a></p>
<p>[15]. <a href="https://github.com/modelcontextprotocol/conformance" target="_blank" rel="noopener noreferrer" class="">MCP Conformance Testing conformance</a></p>
<p>[16]. <a href="https://github.com/modelcontextprotocol/inspector" target="_blank" rel="noopener noreferrer" class="">MCP Inspector</a></p>
<br>
<p>Organizer: Changlu  Created: 2026.10.5  Updated: 2026.10.5</p>]]></content:encoded>
            <category>AgentForge</category>
            <category>General ReAct Agent Layer</category>
            <category>Function Calling</category>
            <category>MCP</category>
        </item>
        <item>
            <title><![CDATA[MCP Protocol to Function Calling Protocol 01, MCP Protocol Explained - Historical Evolution and Underlying Principles]]></title>
            <link>https://agentforge-tech.github.io/AgentForge/en/blog/02、通用ReActagent层/functioncall核心扩展业务tool/mcp/MCP协议转换FunctionCall协议01、MCP协议详解-历史演进与底层原理</link>
            <guid>https://agentforge-tech.github.io/AgentForge/en/blog/02、通用ReActagent层/functioncall核心扩展业务tool/mcp/MCP协议转换FunctionCall协议01、MCP协议详解-历史演进与底层原理</guid>
            <pubDate>Fri, 09 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[In one sentence: MCP (Model Context Protocol) is an open standard open-sourced by Anthropic at the end of 2024 and now governed by the Agentic AI Foundation under the Linux Foundation. It uses a unified JSON-RPC protocol to decouple "tools/resources/prompts" from every AI application and turn them into pluggable Servers, thereby reducing the integration cost of "AI application × external capability" from M×N to M+N.]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>In one sentence: MCP (Model Context Protocol) is an open standard open-sourced by Anthropic at the end of 2024 and now governed by the Agentic AI Foundation under the Linux Foundation. It uses a unified JSON-RPC protocol to decouple "tools/resources/prompts" from every AI application and turn them into pluggable Servers, thereby reducing the integration cost of "AI application × external capability" from M×N to M+N.</p>
</blockquote>
<!-- -->
<br>
<p>This article is a special topic on the MCP protocol: it starts from its <strong>origins and version history</strong>, then gradually unfolds <strong>core concepts, protocol design, and Client/Server underlying principles</strong>, and provides <strong>a quick Java-based Client/Server setup</strong> and <strong>a design workflow for integrating with Agents</strong>, finally landing on <strong>security mechanisms, extension governance, and design trade-offs</strong>.</p>
<blockquote>
<p>Official entry points: <a href="https://modelcontextprotocol.io/" target="_blank" rel="noopener noreferrer" class="">specification modelcontextprotocol.io</a> | <a href="https://blog.modelcontextprotocol.io/" target="_blank" rel="noopener noreferrer" class="">official blog</a> | <a href="https://github.com/modelcontextprotocol" target="_blank" rel="noopener noreferrer" class="">GitHub organization</a> | <a href="https://registry.modelcontextprotocol.io/" target="_blank" rel="noopener noreferrer" class="">official registry</a> | <a href="https://github.com/modelcontextprotocol/java-sdk" target="_blank" rel="noopener noreferrer" class="">Java SDK</a></p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-background-and-problem-introduction">1. Background and Problem Introduction<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#1-background-and-problem-introduction" class="hash-link" aria-label="Direct link to 1. Background and Problem Introduction" title="Direct link to 1. Background and Problem Introduction" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="11-scenario-driven-a-real-integration-challenge">1.1. Scenario-driven: A Real Integration Challenge<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#11-scenario-driven-a-real-integration-challenge" class="hash-link" aria-label="Direct link to 1.1. Scenario-driven: A Real Integration Challenge" title="Direct link to 1.1. Scenario-driven: A Real Integration Challenge" translate="no">​</a></h3>
<p>While developing an AI Agent product, we ran into this concrete problem:</p>
<blockquote>
<p>Users want the Agent to check the weather, query a database, read GitHub, send DingTalk messages... and these capabilities have already been written by different teammates using different languages and frameworks as a bunch of "tool services." Some are written in Python, some in Node, and some are simply a shell script.<br>
<!-- -->The problem is: <strong>every time a tool is integrated, the Agent application must write adapter code for it</strong>. As tools multiply, the application side becomes a messy stew: one moment it needs to spawn a Python process, another it needs to wrap HTTP, another it needs to write JSON Schema. Switch model vendors, and the Schema format differs again—start over from scratch.</p>
</blockquote>
<p>This is not the pain point of a single product, but a pain point of the entire industry. MCP was born precisely for it.</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="12-the-m--n-integration-disaster-of-function-calling">1.2. The "M × N Integration Disaster" of Function Calling<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#12-the-m--n-integration-disaster-of-function-calling" class="hash-link" aria-label="Direct link to 1.2. The &quot;M × N Integration Disaster&quot; of Function Calling" title="Direct link to 1.2. The &quot;M × N Integration Disaster&quot; of Function Calling" translate="no">​</a></h3>
<p>Before MCP appeared, the mainstream approach was each model vendor's <strong>Function Calling / Tool Use</strong>:</p>
<ul>
<li class="">OpenAI uses <code>tools[].function.{name,description,parameters}</code></li>
<li class="">Anthropic uses <code>tools[].{name,description,input_schema}</code></li>
<li class="">Each vendor's prompt templates, forced-call parameters, and parallel-call capabilities are not entirely consistent.</li>
</ul>
<p>This gives rise to a typical <strong>M × N problem</strong>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">M 个 AI 应用（Host / Agent 框架）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ×</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">N 个外部工具（DB、GitHub、Slack、文件系统……）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        =</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">M × N 份适配代码</span><br></div></code></pre></div></div>
<p>Every time a new tool is added, it must be integrated repeatedly across M applications; every time a new application is added, it must re-integrate with N tools. <strong>Costs grow squarely with scale.</strong></p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="13-design-inspiration-an-analogy-from-lsp">1.3. Design Inspiration: An Analogy from LSP<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#13-design-inspiration-an-analogy-from-lsp" class="hash-link" aria-label="Direct link to 1.3. Design Inspiration: An Analogy from LSP" title="Direct link to 1.3. Design Inspiration: An Analogy from LSP" translate="no">​</a></h3>
<p>MCP's architectural paradigm is directly inherited from <strong>LSP (Language Server Protocol)</strong>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">LSP：M 个编辑器 × N 种编程语言  →  M + N（协议居中）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">MCP：M 个 AI 应用 × N 个外部能力 →  M + N（协议居中）</span><br></div></code></pre></div></div>
<p>Just as LSP decouples "editors" from "language services" through a unified protocol, MCP decouples "AI applications" from "external capabilities" through a unified protocol. This analogy is the key to understanding all of MCP's design—<strong>Host handles orchestration, Server handles capabilities, and the protocol handles communication</strong>.</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="14-questions-this-article-answers">1.4. Questions This Article Answers<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#14-questions-this-article-answers" class="hash-link" aria-label="Direct link to 1.4. Questions This Article Answers" title="Direct link to 1.4. Questions This Article Answers" translate="no">​</a></h3>
<ol>
<li class="">Where did MCP come from? How has its governance evolved?<br></li>
<li class="">What versions does it have? What changed in each version?<br></li>
<li class="">What is the full picture of the current (2026-07-28) protocol design?<br></li>
<li class="">How does an MCP Client / Server run internally?<br></li>
<li class="">What trade-offs lie behind these designs?</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-origins-and-version-history">2. Origins and Version History<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#2-origins-and-version-history" class="hash-link" aria-label="Direct link to 2. Origins and Version History" title="Direct link to 2. Origins and Version History" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-release-and-open-sourcing">2.1. Release and Open-Sourcing<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#21-release-and-open-sourcing" class="hash-link" aria-label="Direct link to 2.1. Release and Open-Sourcing" title="Direct link to 2.1. Release and Open-Sourcing" translate="no">​</a></h3>
<ul>
<li class=""><strong>2024-11-05</strong>: The first specification version of MCP was released, defining the client-server architecture, JSON-RPC 2.0, the three major primitives <code>tools</code>/<code>resources</code>/<code>prompts</code>, and two transports: <code>stdio</code> and <code>HTTP+SSE</code>.</li>
<li class=""><strong>2024-11-25</strong>: Anthropic <strong>publicly announced and open-sourced</strong> MCP, releasing Python / TypeScript SDKs and reference Servers for Google Drive, Slack, GitHub, Git, Postgres, etc.</li>
</ul>
<blockquote>
<p><strong>Note</strong>: The version number <code>2024-11-05</code> predates the public announcement date <code>2024-11-25</code>. This is because MCP uses <strong>date-based version numbers</strong>, meaning "<strong>the date of the last backward-incompatible change</strong>", not the release date.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-version-numbers-and-status-rules">2.2. Version Numbers and Status Rules<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#22-version-numbers-and-status-rules" class="hash-link" aria-label="Direct link to 2.2. Version Numbers and Status Rules" title="Direct link to 2.2. Version Numbers and Status Rules" translate="no">​</a></h3>
<ul>
<li class="">Version identifiers take the form <code>YYYY-MM-DD</code>; the protocol version <strong>does not increment for backward-compatible updates</strong>.</li>
<li class="">Each revision has three states: <strong>Draft</strong>, <strong>Current</strong>, and <strong>Final</strong>.</li>
<li class="">An individual feature can also be marked <strong>Deprecated</strong>, subject to a formal <strong>feature lifecycle and deprecation policy</strong> (introduced in 2026-07-28, with a deprecation window of at least 12 months).</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="23-version-timeline-and-adoption-milestones">2.3. Version Timeline and Adoption Milestones<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#23-version-timeline-and-adoption-milestones" class="hash-link" aria-label="Direct link to 2.3. Version Timeline and Adoption Milestones" title="Direct link to 2.3. Version Timeline and Adoption Milestones" translate="no">​</a></h3>
<table><thead><tr><th>Date</th><th>Event</th><th>Type</th></tr></thead><tbody><tr><td>2024-11-05</td><td>First specification <code>2024-11-05</code>: client-server, JSON-RPC 2.0, tools/resources/prompts, stdio + HTTP+SSE</td><td>Specification revision</td></tr><tr><td>2024-11-25</td><td>Anthropic announces and open-sources MCP, releases Python / TS SDKs and reference Servers</td><td>Release</td></tr><tr><td>2025-03-19</td><td>Microsoft introduces MCP support in Copilot Studio</td><td>Adoption</td></tr><tr><td>2025-03-26</td><td>Specification <code>2025-03-26</code>: OAuth 2.1 authorization, Streamable HTTP replaces HTTP+SSE, tool annotations, audio, completions, JSON-RPC batching</td><td>Specification revision</td></tr><tr><td>2025-03-26</td><td>OpenAI introduces MCP in the Agents SDK</td><td>Adoption</td></tr><tr><td>2025-04-09</td><td>Google Gemini commits to supporting MCP</td><td>Adoption</td></tr><tr><td>2025-05-19</td><td>Microsoft fully supports MCP at Build 2025; Microsoft and GitHub join the steering committee</td><td>Governance/Adoption</td></tr><tr><td>2025-05-21</td><td>OpenAI supports remote MCP Servers in the Responses API and joins the steering committee</td><td>Adoption/Governance</td></tr><tr><td>2025-06-18</td><td>Specification <code>2025-06-18</code>: structured tool output, elicitation, resource links; removes JSON-RPC batching; <code>MCP-Protocol-Version</code> header</td><td>Specification revision</td></tr><tr><td>2025-09-05</td><td>Official PHP SDK released</td><td>Ecosystem</td></tr><tr><td>2025-09-08</td><td>Official MCP Registry preview released</td><td>Ecosystem</td></tr><tr><td>2025-11-25</td><td>Specification <code>2025-11-25</code> (first anniversary): OpenID Connect Discovery, icons, standardized enum elicitation, sampling supporting tool calls, experimental Tasks, JSON Schema 2020-12</td><td>Specification revision</td></tr><tr><td>2025-12-09</td><td>Anthropic donates MCP to the newly established Agentic AI Foundation (AAIF) under the Linux Foundation</td><td>Governance</td></tr><tr><td>2026-01-26</td><td>MCP Apps released as the first official extension</td><td>Extension</td></tr><tr><td>2026-05-21</td><td>Release Candidate for the next version <code>2026-07-28</code></td><td>Specification revision</td></tr><tr><td>2026-06-29</td><td>Four Tier 1 SDKs release Beta versions implementing the candidate</td><td>Ecosystem</td></tr><tr><td>2026-07-28</td><td>Specification <code>2026-07-28</code> becomes <strong>Current</strong>; four Tier 1 SDKs support it the same day</td><td>Specification revision</td></tr></tbody></table>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="24-key-design-changes-in-each-version">2.4. Key Design Changes in Each Version<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#24-key-design-changes-in-each-version" class="hash-link" aria-label="Direct link to 2.4. Key Design Changes in Each Version" title="Direct link to 2.4. Key Design Changes in Each Version" translate="no">​</a></h3>
<p><strong><code>2025-03-26</code>: A Big Leap in Transport and Authorization</strong></p>
<ul>
<li class="">Adds the <strong>OAuth 2.1</strong> authorization framework;</li>
<li class="">Replaces HTTP+SSE with <strong>Streamable HTTP</strong> (at the time sessions were still retained: the <code>Mcp-Session-Id</code> header, opening a separate SSE stream via GET, and recovery via <code>Last-Event-ID</code>);</li>
<li class="">Adds tool annotations, audio content, argument completions, and JSON-RPC batching.</li>
</ul>
<p><strong><code>2025-06-18</code>: Structured Output and Security Positioning</strong></p>
<ul>
<li class="">Adds <strong>structured tool output</strong> (<code>outputSchema</code> / <code>structuredContent</code>), <strong>elicitation</strong> (the server requesting input from the user), and <strong>resource links</strong> in tool results;</li>
<li class="">Clearly positions MCP Server as an <strong>OAuth Resource Server</strong> and requires <strong>Resource Indicators (RFC 8707)</strong>;</li>
<li class=""><strong>Removes JSON-RPC batching</strong>;</li>
<li class="">Requires subsequent HTTP requests to carry the <code>MCP-Protocol-Version</code> header.</li>
</ul>
<p><strong><code>2025-11-25</code>: The One-Year Anniversary Version</strong></p>
<ul>
<li class="">Enhances authorization server discovery to <strong>OpenID Connect Discovery 1.0</strong>, supporting incremental authorization scope consent via <code>WWW-Authenticate</code>;</li>
<li class="">Tools/resources/templates/prompts support <strong>icon metadata</strong>;</li>
<li class="">Elicitation enums are standardized into titled/untitled/single/multi-select and URL modes;</li>
<li class="">Sampling supports tool calls; adds the <strong>OAuth Client ID Metadata Documents</strong> registration mechanism;</li>
<li class="">Introduces <strong>experimental Tasks</strong>; establishes <strong>JSON Schema 2020-12</strong> as the default dialect.</li>
</ul>
<p><strong><code>2026-07-28</code>: The Stateless Paradigm Revision (Current Version)</strong></p>
<p>Called "the largest revision since release," its highlights: <strong>stateless core, removal of handshake and session, <code>server/discover</code>, MRTR, subscription streams, extension framework, authorization hardening, and a formal deprecation policy</strong>. See Chapter 4 for details.</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="25-governance">2.5. Governance<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#25-governance" class="hash-link" aria-label="Direct link to 2.5. Governance" title="Direct link to 2.5. Governance" translate="no">​</a></h3>
<ul>
<li class="">Starting in 2025, Microsoft, GitHub, OpenAI, and others successively joined the MCP steering committee;</li>
<li class="">On 2025-12-09, Anthropic <strong>donated</strong> MCP to the newly established <strong>Agentic AI Foundation (AAIF)</strong> under the Linux Foundation (co-founded by Anthropic, Block, and OpenAI), with an official statement that the governance model remains unchanged;</li>
<li class="">The specification adopts the <strong>SEP (Specification Enhancement Proposal)</strong> PR workflow.</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-core-concepts">3. Core Concepts<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#3-core-concepts" class="hash-link" aria-label="Direct link to 3. Core Concepts" title="Direct link to 3. Core Concepts" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-the-three-roles-host--client--server">3.1. The Three Roles: Host / Client / Server<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#31-the-three-roles-host--client--server" class="hash-link" aria-label="Direct link to 3.1. The Three Roles: Host / Client / Server" title="Direct link to 3.1. The Three Roles: Host / Client / Server" translate="no">​</a></h3>
<p>MCP adopts a <strong>client-host-server</strong> architecture; note there are two "C"s here, which is easy to confuse:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">┌──────────────────────────────────────────────────────────┐</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│  Host 进程（AI 应用，如 Claude Desktop / IDE / Agent 框架） │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── Client 1  ──1:1──►  Server 1（本地文件 &amp; Git）       │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── Client 2  ──1:1──►  Server 2（数据库）               │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   └── Client 3  ──1:1──►  Server 3（远程 SaaS API）        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">└──────────────────────────────────────────────────────────┘</span><br></div></code></pre></div></div>
<ul>
<li class=""><strong>Host</strong>: Creates and manages multiple Clients, controls connection permissions and lifecycle, enforces security policies and user authorization decisions, and aggregates context for the LLM.</li>
<li class=""><strong>Client</strong>: Created by the Host, <strong>strictly 1:1 with a single Server</strong>; attaches the protocol version and capabilities to each request; routes messages bidirectionally; manages subscriptions and notifications; and maintains security boundaries between Servers.</li>
<li class=""><strong>Server</strong>: Exposes concrete capabilities externally through primitives, independent with a single responsibility; can be a local process or a remote service.</li>
</ul>
<blockquote>
<p><strong>Key point</strong>: MCP is a <strong>stateless protocol</strong>—each request carries all the information needed for processing (version, capabilities, identity), and the Server must not rely on the connection or previous requests to infer state.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-the-three-server-primitives-and-control">3.2. The Three Server Primitives and Control<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#32-the-three-server-primitives-and-control" class="hash-link" aria-label="Direct link to 3.2. The Three Server Primitives and Control" title="Direct link to 3.2. The Three Server Primitives and Control" translate="no">​</a></h3>
<table><thead><tr><th>Primitive</th><th>Controlled by</th><th>Purpose</th><th>Corresponding methods</th></tr></thead><tbody><tr><td><strong>Tools</strong></td><td><strong>model-controlled</strong></td><td>Functions callable by the LLM</td><td><code>tools/list</code>, <code>tools/call</code></td></tr><tr><td><strong>Resources</strong></td><td><strong>application-controlled</strong></td><td>Readable context, identified by URI</td><td><code>resources/list</code>, <code>resources/read</code></td></tr><tr><td><strong>Prompts</strong></td><td><strong>user-controlled</strong></td><td>Reusable prompt templates</td><td><code>prompts/list</code>, <code>prompts/get</code></td></tr></tbody></table>
<p>Design-wise, the "control" of the three is clearly different: tools are invoked by the model's own decision, resources are injected into context by the application's decision, and prompts are explicitly selected by the user.</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-four-design-principles">3.3. Four Design Principles<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#33-four-design-principles" class="hash-link" aria-label="Direct link to 3.3. Four Design Principles" title="Direct link to 3.3. Four Design Principles" translate="no">​</a></h3>
<ol>
<li class=""><strong>Servers should be extremely easy to build</strong>: complex orchestration is borne by the Host, and the Server focuses on a single capability.</li>
<li class=""><strong>Servers should be highly composable</strong>: multiple Servers can be seamlessly composed, sharing the same protocol.</li>
<li class=""><strong>Servers cannot read the entire conversation, nor can they see other Servers</strong>: the full conversation history stays only in the Host; Servers are isolated from each other, and cross-Server interaction is controlled by the Host.</li>
<li class=""><strong>Features can be added incrementally</strong>: the core protocol is minimal, and other capabilities are declared and negotiated on demand.</li>
</ol>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="34-capability-negotiation">3.4. Capability Negotiation<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#34-capability-negotiation" class="hash-link" aria-label="Direct link to 3.4. Capability Negotiation" title="Direct link to 3.4. Capability Negotiation" translate="no">​</a></h3>
<p>Capability negotiation is a core mechanism of MCP, but <strong>the negotiation method underwent a fundamental change in 2026-07-28</strong>:</p>
<ul>
<li class=""><strong>Old model (≤ 2025-11-25)</strong>: through the <code>initialize</code> handshake, capabilities for the entire session are exchanged and determined in one go.</li>
<li class=""><strong>New model (2026-07-28)</strong>: no handshake; the client declares capabilities in <code>_meta.io.modelcontextprotocol/clientCapabilities</code> on <strong>each request</strong>; the server announces its supported versions and capabilities in one shot via <a href="https://modelcontextprotocol.io/specification/2026-07-28/server/discover" target="_blank" rel="noopener noreferrer" class=""><code>server/discover</code></a>.</li>
</ul>
<p>Typical capabilities: server <code>tools</code> / <code>resources</code> / <code>prompts</code> / <code>logging</code> / <code>completions</code>; client <code>roots</code> / <code>sampling</code> / <code>elicitation</code>.</p>
<blockquote>
<p><strong>Important rule</strong>: A server <strong>MUST NOT</strong> rely on capabilities not declared by the client; if handling a request requires a capability the client has not declared, the server <strong>MUST</strong> return <code>-32021 MissingRequiredClientCapability</code> and list them in <code>data.requiredCapabilities</code>.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-core-protocol-design">4. Core Protocol Design<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#4-core-protocol-design" class="hash-link" aria-label="Direct link to 4. Core Protocol Design" title="Direct link to 4. Core Protocol Design" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="41-message-model-json-rpc-20">4.1. Message Model: JSON-RPC 2.0<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#41-message-model-json-rpc-20" class="hash-link" aria-label="Direct link to 4.1. Message Model: JSON-RPC 2.0" title="Direct link to 4.1. Message Model: JSON-RPC 2.0" translate="no">​</a></h3>
<p>All MCP messages follow <strong>JSON-RPC 2.0</strong>, and messages <strong>MUST</strong> be UTF-8. Message types:</p>
<div class="language-jsonc codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-jsonc codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">// 1) Request：请求，MUST 带非 null 的 id（不得与未响应的请求 id 重复）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { /* ... */ } }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">// 2) Result Response：成功响应，MUST 带 resultType（2026-07-28 起）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">{ "jsonrpc": "2.0", "id": 1, "result": { "resultType": "complete", "content": [ /* ... */ ] } }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">// 3) Error Response：失败响应，MUST 带 code 与 message</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">{ "jsonrpc": "2.0", "id": 1, "error": { "code": -32602, "message": "Invalid params" } }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">// 4) Notification：通知，MUST NOT 带 id</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">{ "jsonrpc": "2.0", "method": "notifications/tools/list_changed" }</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Design point</strong>: MCP specifies that "<strong>the client only sends requests/notifications, and the server only sends responses/notifications</strong>"—the server does not proactively send requests (after 2026-07-28), and the client does not send responses. This constraint is the cornerstone of the stateless and unidirectional-flow design.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="42-_meta-request-level-protocol-metadata">4.2. <code>_meta</code>: Request-Level Protocol Metadata<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#42-_meta-request-level-protocol-metadata" class="hash-link" aria-label="Direct link to 42-_meta-request-level-protocol-metadata" title="Direct link to 42-_meta-request-level-protocol-metadata" translate="no">​</a></h3>
<p>All protocol metadata is carried with the <strong>message body</strong> (the transport layer may optionally mirror it into envelope headers). The <code>_meta</code> fields of a client request:</p>
<table><thead><tr><th>Key</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td><code>io.modelcontextprotocol/protocolVersion</code></td><td>string</td><td>Yes</td><td>The protocol version used for this request</td></tr><tr><td><code>io.modelcontextprotocol/clientCapabilities</code></td><td>object</td><td>Yes</td><td>Client-related capabilities</td></tr><tr><td><code>io.modelcontextprotocol/clientInfo</code></td><td>object</td><td>No</td><td>Client name and version (SHOULD)</td></tr><tr><td><code>io.modelcontextprotocol/logLevel</code></td><td>string</td><td>No</td><td>Minimum log level for this request</td></tr></tbody></table>
<p>The server <strong>SHOULD</strong> return <code>io.modelcontextprotocol/serverInfo</code> in the <code>_meta</code> of every result. In addition, <code>traceparent</code> / <code>tracestate</code> / <code>baggage</code> are reserved for OpenTelemetry tracing context.</p>
<blockquote>
<p><strong>Key point</strong>: The absence of <code>protocolVersion</code> and <code>clientCapabilities</code> constitutes a <strong>malformed request</strong>, and the server <strong>MUST</strong> reject it with <code>-32602</code> (returning <code>400 Bad Request</code> over HTTP). This is the concrete embodiment of "requests are self-describing and do not depend on connection state."</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="43-resulttype-polymorphic-result-design">4.3. <code>resultType</code>: Polymorphic Result Design<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#43-resulttype-polymorphic-result-design" class="hash-link" aria-label="Direct link to 43-resulttype-polymorphic-result-design" title="Direct link to 43-resulttype-polymorphic-result-design" translate="no">​</a></h3>
<p>2026-07-28 introduces a <strong>required</strong> <code>resultType</code> for all results:</p>
<ul>
<li class=""><code>"complete"</code>: normal completion, with <code>result</code> containing the final content;</li>
<li class=""><code>"input_required"</code>: the client needs to supply additional input (see 4.8 MRTR);</li>
<li class="">Extensions may add other <code>resultType</code> values, but they must come from the core set or a declared extension;</li>
<li class="">If a client encounters an unrecognized <code>resultType</code>, it <strong>MUST</strong> treat it as invalid;</li>
<li class="">For older servers that do not return <code>resultType</code>, the client <strong>MUST</strong> treat it as <code>"complete"</code>.</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="44-error-model-and-error-codes">4.4. Error Model and Error Codes<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#44-error-model-and-error-codes" class="hash-link" aria-label="Direct link to 4.4. Error Model and Error Codes" title="Direct link to 4.4. Error Model and Error Codes" translate="no">​</a></h3>
<p>MCP divides errors into two categories, <strong>handled differently</strong>:</p>
<table><thead><tr><th>Type</th><th>Meaning</th><th>Returned via</th><th>Model can self-correct</th></tr></thead><tbody><tr><td><strong>Protocol Error</strong></td><td>Request structure problems (unknown tool, malformed request, server error)</td><td>JSON-RPC <code>error</code> object</td><td>Usually can't</td></tr><tr><td><strong>Tool Execution Error</strong></td><td>Business/API/validation errors</td><td><code>result.isError = true</code> + text</td><td><strong>Can</strong></td></tr></tbody></table>
<p>Error code partition strategy:</p>
<ul>
<li class=""><code>-32700</code>, <code>-32600</code>~<code>-32603</code>: standard JSON-RPC generic errors;</li>
<li class=""><code>-32000</code>~<code>-32019</code>: <strong>legacy</strong> implementation-defined range (new implementations should no longer allocate from it);</li>
<li class=""><code>-32020</code>~<code>-32099</code>: <strong>reserved for the MCP specification</strong> (see <a href="https://modelcontextprotocol.io/specification/2026-07-28/schema" target="_blank" rel="noopener noreferrer" class="">Schema error definitions</a>). Defined in 2026-07-28:</li>
</ul>
<table><thead><tr><th>Code</th><th>Name</th></tr></thead><tbody><tr><td><code>-32020</code></td><td><code>HeaderMismatch</code></td></tr><tr><td><code>-32021</code></td><td><code>MissingRequiredClientCapability</code></td></tr><tr><td><code>-32022</code></td><td><code>UnsupportedProtocolVersion</code></td></tr></tbody></table>
<p>The historical code <code>-32002</code> (resource not found, ≤2025-11-25) was replaced by <code>-32602</code>, but clients <strong>SHOULD</strong> still remain compatible with older servers' <code>-32002</code>.</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="45-transport-design">4.5. Transport Design<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#45-transport-design" class="hash-link" aria-label="Direct link to 4.5. Transport Design" title="Direct link to 4.5. Transport Design" translate="no">​</a></h3>
<p>Protocol semantics are consistent across all transports; transports are only responsible for "framing, delivery, cancellation, and termination." There are two standard transports:</p>
<p><strong>stdio</strong> (<a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/stdio" target="_blank" rel="noopener noreferrer" class="">spec</a>)</p>
<ul>
<li class="">The Client starts the Server as a subprocess and communicates through its standard streams;</li>
<li class="">Each message is one line of JSON, <strong>with no embedded newlines</strong>;</li>
<li class="">The Server <strong>MUST NOT</strong> write non-MCP messages to <code>stdout</code>; logs go to <code>stderr</code>;</li>
<li class="">Cancellation: send <code>notifications/cancelled</code> (single channel, no per-request stream);</li>
<li class="">Shutdown: close stdin → wait for exit → <code>SIGTERM</code> → <code>SIGKILL</code> (on Windows use <code>TerminateProcess</code>/Job Objects).</li>
</ul>
<p><strong>Streamable HTTP (2026-07-28 form)</strong> (<a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http" target="_blank" rel="noopener noreferrer" class="">spec</a>)</p>
<ul>
<li class="">A single <strong>MCP endpoint</strong>, <strong>POST</strong> only; each request is an independent POST;</li>
<li class="">Request headers MUST include <code>Accept: application/json, text/event-stream</code>, <code>MCP-Protocol-Version</code>, <code>Mcp-Method</code>, and for <code>tools/call</code>/<code>resources/read</code>/<code>prompts/get</code> also <code>Mcp-Name</code>;</li>
<li class="">The response can be a single JSON, or a <strong>request-level SSE stream</strong> (notifications first, with the final response last);</li>
<li class="">Cancellation = closing the response stream for that request;</li>
<li class="">2026-07-28 <strong>removes</strong>: the HTTP GET stream, <code>Mcp-Session-Id</code>, and <code>Last-Event-ID</code> recoverability.</li>
</ul>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">POST /mcp HTTP/1.1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Content-Type: application/json</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Accept: application/json, text/event-stream</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">MCP-Protocol-Version: 2026-07-28</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Mcp-Method: tools/call</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Mcp-Name: get_weather</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">{ "jsonrpc": "2.0", "id": 1, "method": "tools/call",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  "params": { "name": "get_weather", "arguments": { "location": "Seattle, WA" },</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    "_meta": { "io.modelcontextprotocol/protocolVersion": "2026-07-28",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">               "io.modelcontextprotocol/clientCapabilities": {} } } }</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Deprecation note</strong>: The old HTTP+SSE (2024-11-05) was replaced on 2025-03-26 and officially classified as <strong>Deprecated</strong> in 2026-07-28; new implementations should not adopt it.</p>
<p><strong>Note</strong>: <code>x-mcp-header</code> allows mirroring primitive-typed parameters (string/integer/boolean, not number) into <code>Mcp-Param-{Name}</code> headers to facilitate gateway routing; however, headers are visible to the intermediate network, and <strong>must not</strong> be marked with passwords/Tokens/PII.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="46-lifecycle-legacy-handshake-vs-modern-per-request">4.6. Lifecycle: legacy Handshake vs. modern Per-Request<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#46-lifecycle-legacy-handshake-vs-modern-per-request" class="hash-link" aria-label="Direct link to 4.6. Lifecycle: legacy Handshake vs. modern Per-Request" title="Direct link to 4.6. Lifecycle: legacy Handshake vs. modern Per-Request" translate="no">​</a></h3>
<p><strong>Legacy (≤ 2025-11-25)</strong>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">initialize → 服务端返回能力 → notifications/initialized → 正常操作 → 关闭</span><br></div></code></pre></div></div>
<p><code>initialize</code> request/response structure:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// 请求</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"method"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"initialize"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"params"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"protocolVersion"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2025-06-18"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">              </span><span class="token property">"capabilities"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"roots"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"listChanged"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"sampling"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">              </span><span class="token property">"clientInfo"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"ExampleClient"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"version"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"1.0.0"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// 响应</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"result"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"protocolVersion"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2025-06-18"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">              </span><span class="token property">"capabilities"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"tools"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"listChanged"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">              </span><span class="token property">"serverInfo"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"ExampleServer"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"version"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"1.0.0"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p><strong>Modern (2026-07-28)</strong>: no handshake; version and capabilities are carried in the <code>_meta</code> of each request; <code>server/discover</code> is optional and is used to obtain the version/capabilities/identity in one go.</p>
<p><strong>Dual-era compatibility</strong> is a real necessity (old and new Servers coexist), and the official detection mechanism is given:</p>
<ul>
<li class="">stdio: first send <code>server/discover</code>; success → modern; a non-modern error or timeout → fall back to <code>initialize</code>;</li>
<li class="">HTTP: first send a modern request; on <code>400</code>, inspect the response body first—if it is a recognizable modern error → modern; otherwise fall back to <code>initialize</code>.</li>
</ul>
<blockquote>
<p><strong>Note</strong>: Era determination is a <strong>server property</strong> and should be cached (by process for stdio, by origin for HTTP) to avoid probing on every request.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="47-stateless-design-core-of-2026-07-28">4.7. Stateless Design (Core of 2026-07-28)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#47-stateless-design-core-of-2026-07-28" class="hash-link" aria-label="Direct link to 4.7. Stateless Design (Core of 2026-07-28)" title="Direct link to 4.7. Stateless Design (Core of 2026-07-28)" translate="no">​</a></h3>
<table><thead><tr><th>Change</th><th>Description</th></tr></thead><tbody><tr><td>Removes session and <code>Mcp-Session-Id</code></td><td>Requests can be handled by any Server instance, suitable for stateless deployment</td></tr><tr><td>Removes the <code>initialize</code>/<code>initialized</code> handshake</td><td>Version and capabilities move to per-request <code>_meta</code></td></tr><tr><td>Adds <code>server/discover</code></td><td>Announces supportedVersions / capabilities / identity in one go</td></tr><tr><td>List endpoints no longer vary by connection</td><td><code>tools/list</code> does not vary by connection, but can vary by authorization credentials</td></tr></tbody></table>
<p>The direct requirement of statelessness: <strong>state across calls</strong> must be explicitly returned by the Server as a handle (such as <code>basket_id</code>), and passed back by the client as an ordinary argument in subsequent calls. The protocol itself has no concept of a "state handle"—from the wire's perspective, it is just an ordinary string.</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="48-mrtr-multi-round-trip-requests">4.8. MRTR: Multi Round-Trip Requests<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#48-mrtr-multi-round-trip-requests" class="hash-link" aria-label="Direct link to 4.8. MRTR: Multi Round-Trip Requests" title="Direct link to 4.8. MRTR: Multi Round-Trip Requests" translate="no">​</a></h3>
<p>The old version allowed the Server to <strong>proactively</strong> send requests (<code>sampling/createMessage</code>, <code>elicitation/create</code>, <code>roots/list</code>) through the SSE stream. 2026-07-28 changes this to:</p>
<ul>
<li class="">The Server returns an <code>InputRequiredResult</code> (<code>resultType: "input_required"</code>) in the response, with the <code>inputRequests</code> field describing the required input;</li>
<li class="">After the client fills it in, it replays the original request with a <strong>new id</strong> and carries back the answers in <code>params.inputResponses</code> (optionally passing through server state via <code>requestState</code>).</li>
</ul>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Client ── tools/call(id:1) ──► Server</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Client ◄─ InputRequiredResult（inputRequests: elicitation/create）── Server</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Client ── tools/call(id:2, 原参数 + inputResponses) ──► Server</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Client ◄─ 最终 result ── Server</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: MRTR (<a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/patterns/mrtr" target="_blank" rel="noopener noreferrer" class="">Multi Round-Trip Requests</a>, SEP-2322) transforms "server reverse requests" into "client-initiated retries," making it consistent with the stateless model.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="49-subscriptions-and-notifications">4.9. Subscriptions and Notifications<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#49-subscriptions-and-notifications" class="hash-link" aria-label="Direct link to 4.9. Subscriptions and Notifications" title="Direct link to 4.9. Subscriptions and Notifications" translate="no">​</a></h3>
<p>2026-07-28 replaces the old GET stream and <code>resources/subscribe</code> with a <strong>long-lived POST response stream</strong> <a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/patterns/subscriptions" target="_blank" rel="noopener noreferrer" class=""><code>subscriptions/listen</code></a>:</p>
<ul>
<li class="">The Client declares the subscription types (<code>toolsListChanged</code>, <code>promptsListChanged</code>, <code>resourcesListChanged</code>, <code>resourceSubscriptions</code>);</li>
<li class="">The Server first returns <code>notifications/subscriptions/acknowledged</code>, then pushes change notifications on that stream;</li>
<li class="">Each notification carries <code>_meta.io.modelcontextprotocol/subscriptionId</code> to associate it with the originating subscription.</li>
</ul>
<blockquote>
<p><strong>Note</strong>: Request-level notifications (<code>notifications/progress</code>, <code>notifications/message</code>) only travel on <strong>the request's own response stream</strong>. In addition, 2026-07-28 removes SSE recoverability, so a broken stream means the request is lost.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="410-listing-and-caching">4.10. Listing and Caching<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#410-listing-and-caching" class="hash-link" aria-label="Direct link to 4.10. Listing and Caching" title="Direct link to 4.10. Listing and Caching" translate="no">​</a></h3>
<ul>
<li class=""><a href="https://modelcontextprotocol.io/specification/2026-07-28/server/tools" target="_blank" rel="noopener noreferrer" class=""><code>tools/list</code></a>, <code>prompts/list</code>, <code>resources/list</code>, etc. support <strong>pagination</strong> (<code>cursor</code> / <code>nextCursor</code>) and <strong>caching</strong>;</li>
<li class="">2026-07-28 requires list/read results to carry <code>ttlMs</code> (freshness hint) and <code>cacheScope</code> (<code>public</code>/<code>private</code>, controlling whether shared intermediaries can cache);</li>
<li class="">The Server <strong>SHOULD</strong> sort stably to facilitate client caching and LLM Prompt Cache hits;</li>
<li class="">Tool definitions include <code>name</code>, <code>title</code>, <code>description</code>, <code>inputSchema</code> (default JSON Schema 2020-12), optional <code>outputSchema</code>, <code>icons</code>, <code>annotations</code>.</li>
</ul>
<p><strong>Tool definition and invocation example:</strong></p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// tools/list → tools[]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"title"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Weather Information Provider"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"description"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Get current weather information for a location"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"inputSchema"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"object"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"properties"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"location"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"string"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"description"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"City name or zip code"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"required"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"location"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"outputSchema"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"object"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"properties"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"temperature"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"number"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// tools/call 成功响应（非结构化文本 + 结构化内容并存）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"result"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"resultType"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"complete"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"text"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"text"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"{\"temperature\": 22.5, \"conditions\": \"Partly cloudy\"}"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"structuredContent"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"temperature"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">22.5</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"conditions"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Partly cloudy"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"isError"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">false</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Note</strong>: Tool names are 1~128 characters, case-sensitive, allow only <code>A-Za-z0-9_-</code> and <code>.</code>, and are <strong>unique within a single Server</strong>; <code>inputSchema</code> <strong>MUST</strong> be a valid JSON Schema (cannot be <code>null</code>); for tools with no parameters, <code>{ "type": "object", "additionalProperties": false }</code> is recommended.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-underlying-principles-of-a-core-mcp-client--server">5. Underlying Principles of a Core MCP Client / Server<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#5-underlying-principles-of-a-core-mcp-client--server" class="hash-link" aria-label="Direct link to 5. Underlying Principles of a Core MCP Client / Server" title="Direct link to 5. Underlying Principles of a Core MCP Client / Server" translate="no">​</a></h2>
<blockquote>
<p><strong>This section answers</strong>: Setting aside frameworks, how exactly does an MCP client/server run internally?</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="51-message-encodingdecoding-and-request-correlation">5.1. Message Encoding/Decoding and Request Correlation<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#51-message-encodingdecoding-and-request-correlation" class="hash-link" aria-label="Direct link to 5.1. Message Encoding/Decoding and Request Correlation" title="Direct link to 5.1. Message Encoding/Decoding and Request Correlation" translate="no">​</a></h3>
<p>Whether stdio or HTTP, the client core is a routing table of <strong><code>id → pending Future</code></strong>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">发送请求：  id = nextId++; pending.put(id, future);  transport.send(jsonRpcRequest)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">收到消息：  if 有 id 且 有 result/error  → pending.remove(id).complete(...)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">           if 无 id 且 method=notifications/* → 交给通知分发器</span><br></div></code></pre></div></div>
<p>Taking LangChain4j as an example, <code>McpOperationHandler</code> internally is a <code>Map&lt;Long, CompletableFuture&lt;JsonNode&gt;&gt;</code>: when it receives a message "with an id and with result/error," it completes the corresponding Future; when it receives a <code>ping</code> request, it replies with <code>McpPingResponse</code>; when it receives <code>notifications/message</code>, it forwards it to the log consumer.</p>
<blockquote>
<p><strong>Key point</strong>: The protocol is <strong>asynchronous request-response</strong>, but actual usage usually exposes synchronous methods (<code>CompletableFuture.get()</code> or blocking wait), so "timeouts" must be enforced on the client side—this is explicitly recommended by the MCP specification.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="52-client-layering">5.2. Client Layering<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#52-client-layering" class="hash-link" aria-label="Direct link to 5.2. Client Layering" title="Direct link to 5.2. Client Layering" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">┌──────────────────────────────────────────────┐</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│  Client / Protocol 层                          │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│  版本协商、能力声明、id→Future 路由、超时       │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├──────────────────────────────────────────────┤</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│  Transport 层（stdio / Streamable HTTP）       │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│  消息分帧、收发、取消、关闭                     │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">└──────────────────────────────────────────────┘</span><br></div></code></pre></div></div>
<p>Key points: the choice of sync/async, timeouts and cancellation, dual-era detection and caching, subprocess restart and request retry.</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="53-transport-implementation-breakdown">5.3. Transport Implementation Breakdown<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#53-transport-implementation-breakdown" class="hash-link" aria-label="Direct link to 5.3. Transport Implementation Breakdown" title="Direct link to 5.3. Transport Implementation Breakdown" translate="no">​</a></h3>
<p><strong>stdio:</strong> Use <code>ProcessBuilder</code> to start a subprocess, with two background threads reading <code>stdout</code> / writing <code>stdin</code> respectively; messages are framed line by line; <code>stderr</code> is for logs only; when the process exits, the upper layer can restart and retry lost requests.</p>
<p><strong>Streamable HTTP:</strong> Based on an ordinary HTTP client, POST to a single endpoint; based on the response <code>Content-Type</code>, decide between "reading JSON directly" and "reading the SSE stream entry by entry, with the last entry being the final response"; cancellation means closing the response stream.</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="54-server-toolslist-and-toolscall">5.4. Server: <code>tools/list</code> and <code>tools/call</code><a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#54-server-toolslist-and-toolscall" class="hash-link" aria-label="Direct link to 54-server-toolslist-and-toolscall" title="Direct link to 54-server-toolslist-and-toolscall" translate="no">​</a></h3>
<p>What the server needs to do: parse JSON-RPC → dispatch by <code>method</code> → call the business implementation → encapsulate the standard response/error. Key points:</p>
<ul>
<li class="">Declare capabilities: <code>{ "capabilities": { "tools": { "listChanged": true } } }</code> (in the old version in the <code>initialize</code> result, in the new version in the <code>server/discover</code> result);</li>
<li class=""><code>tools/list</code> returns tool definitions (supporting pagination/caching, stable sorting);</li>
<li class=""><code>tools/call</code> executes and returns <code>content</code> / <code>structuredContent</code> / <code>isError</code>;</li>
<li class="">Strictly distinguish "protocol errors (JSON-RPC error)" from "tool execution errors (<code>isError=true</code>)".</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="55-hierarchical-structure-of-reference-implementations">5.5. Hierarchical Structure of Reference Implementations<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#55-hierarchical-structure-of-reference-implementations" class="hash-link" aria-label="Direct link to 5.5. Hierarchical Structure of Reference Implementations" title="Direct link to 5.5. Hierarchical Structure of Reference Implementations" translate="no">​</a></h3>
<p><strong>LangChain4j (Java):</strong></p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">McpToolProvider (ToolProvider)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      │  聚合 1..N 个 McpClient，过滤/改名</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">DefaultMcpClient (McpClient)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      │  管理 JSON-RPC 生命周期（initialize、listTools、callTool）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">McpTransport（Stdio / StreamableHttp / WebSocket / Docker）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      │  只负责消息收发</span><br></div></code></pre></div></div>
<ul>
<li class=""><code>McpToolExecutor</code> implements the framework's <code>ToolExecutor</code>, internally calling <code>McpClient.executeTool(...)</code>;</li>
<li class=""><code>ToolSpecificationHelper</code> handles the conversion between MCP <code>inputSchema</code> ↔ the framework's <code>ToolSpecification</code> (supporting object/array/enum/anyOf).</li>
</ul>
<p><strong>Spring AI (Java):</strong></p>
<ul>
<li class=""><code>SyncMcpToolCallback(client, tool)</code> / <code>AsyncMcpToolCallback</code> adapt MCP tools into <code>ToolCallback</code>;</li>
<li class=""><code>SyncMcpToolCallbackProvider</code> aggregates tools from multiple <code>McpSyncClient</code>s.</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="56-comparison-of-mainstream-integration-patterns">5.6. Comparison of Mainstream "Integration Patterns"<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#56-comparison-of-mainstream-integration-patterns" class="hash-link" aria-label="Direct link to 5.6. Comparison of Mainstream &quot;Integration Patterns&quot;" title="Direct link to 5.6. Comparison of Mainstream &quot;Integration Patterns&quot;" translate="no">​</a></h3>
<p><strong>Approach A: Framework-side tool adapter (Adapter / Provider)—mainstream</strong></p>
<p>Implement an adaptation layer of "MCP tool → framework tool" in the client SDK (i.e., the pattern in 5.5).</p>
<ul>
<li class="">Pros: low integration cost, dynamic discovery, reusing the same loop as the framework's tool system;</li>
<li class="">Cons: requires the framework side to implement a protocol client and Schema mapping.</li>
</ul>
<p><strong>Approach B: MCP gateway / registry aggregation—platformization</strong></p>
<p>Use a gateway (such as AWS Bedrock AgentCore Gateway) or a registry (MCP Registry) to aggregate multiple MCP Servers into a unified entry point.</p>
<ul>
<li class="">Pros: centralized governance, authentication, rate limiting, observability;</li>
<li class="">Cons: introduces gateway operation-and-maintenance costs; adds one more network hop.</li>
</ul>
<p><strong>Approach C: Dynamically materializing into local tools—a shortcut</strong></p>
<p>At runtime, pull <code>tools/list</code> and disguise MCP tools as local methods using dynamic proxies or code generation.</p>
<ul>
<li class="">Pros: transparent to the upper layer;</li>
<li class="">Cons: hard to carry complex JSON Schema parameters; debugging is not intuitive; not recommended for long-term use.</li>
</ul>
<p><strong>Approach D: Upgrading the MCP Server into an independent Runtime / SubAgent</strong></p>
<p>Treat the Server as an independent Agent runtime, which the main Agent calls in a "task delegation" manner (similar to A2A).</p>
<ul>
<li class="">Pros: suitable for complex, stateful, long-running tasks;</li>
<li class="">Cons: heavyweight, deviating from MCP's original intent of a "simple Server."</li>
</ul>
<blockquote>
<p><strong>Conclusion</strong>: For the vast majority of scenarios, choose <strong>Approach A</strong>, which is the highest return-on-investment route.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="57-quickly-building-an-mcp-client--server-in-java">5.7. Quickly Building an MCP Client / Server in Java<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#57-quickly-building-an-mcp-client--server-in-java" class="hash-link" aria-label="Direct link to 5.7. Quickly Building an MCP Client / Server in Java" title="Direct link to 5.7. Quickly Building an MCP Client / Server in Java" translate="no">​</a></h3>
<blockquote>
<p>With the official <strong>MCP Java SDK</strong> (<code>io.modelcontextprotocol.sdk</code>), you can get a Client/Server running in a few dozen lines. Key points: the SDK requires <strong>JDK 17+</strong>; the core module <code>mcp</code> has built-in <strong>STDIO / Streamable HTTP / SSE(legacy)</strong> transports; the client defaults to the JDK <code>HttpClient</code>, and JSON defaults to Jackson; the server core has built-in Servlet transports; it provides both synchronous and asynchronous APIs (asynchronous based on Reactor).</p>
</blockquote>
<p><strong>1) Add the dependency</strong></p>
<div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">dependencyManagement</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">dependencies</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">io.modelcontextprotocol.sdk</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">mcp-bom</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">2.0.1</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">type</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">pom</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">type</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">scope</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">import</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">scope</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">dependencies</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">dependencyManagement</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">io.modelcontextprotocol.sdk</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">mcp</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><br></div></code></pre></div></div>
<p><strong>2) Client: connect to a local stdio Server and call a tool</strong></p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// 1. 传输：启动本地子进程（例如官方 everything server）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ServerParameters</span><span class="token plain"> params </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">ServerParameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"npx"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">args</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"-y"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"@modelcontextprotocol/server-everything"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"dir"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">McpTransport</span><span class="token plain"> transport </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">StdioClientTransport</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">params</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">McpJsonDefaults</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getMapper</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// 2. 同步客户端 + 能力 + 超时</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">McpSyncClient</span><span class="token plain"> client </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">McpClient</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">sync</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">transport</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">requestTimeout</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Duration</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">ofSeconds</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">10</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">capabilities</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ClientCapabilities</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">roots</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">sampling</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">elicitation</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// 3. 初始化（完成版本/能力协商）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">InitializeResult</span><span class="token plain"> init </span><span class="token operator">=</span><span class="token plain"> client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">initialize</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">String</span><span class="token plain"> version </span><span class="token operator">=</span><span class="token plain"> init</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">protocolVersion</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// 4. 发现工具 + 调用工具</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ListToolsResult</span><span class="token plain"> tools </span><span class="token operator">=</span><span class="token plain"> client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">listTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">CallToolResult</span><span class="token plain"> result </span><span class="token operator">=</span><span class="token plain"> client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">callTool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">CallToolRequest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"calculator"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">arguments</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Map</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">of</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"operation"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"add"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"a"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token number">2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"b"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token number">3</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// 5. 优雅关闭</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">closeGracefully</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>Connecting to a remote Server requires only switching the transport (Streamable HTTP):</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">McpTransport</span><span class="token plain"> transport </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">HttpClientStreamableHttpTransport</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"http://your-mcp-server"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">endpoint</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"/mcp"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Note</strong>: <code>listTools()</code> / <code>listResources()</code> / <code>listPrompts()</code> all support pagination; you need to loop until <code>nextCursor == null</code> to obtain the full set of tools.</p>
</blockquote>
<p><strong>3) Server: expose a tool</strong></p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">StdioServerTransportProvider</span><span class="token plain"> transportProvider </span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">StdioServerTransportProvider</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">McpJsonDefaults</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getMapper</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">McpSyncServer</span><span class="token plain"> server </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">McpServer</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">sync</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">transportProvider</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">serverInfo</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"my-server"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"1.0.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">capabilities</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ServerCapabilities</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">var</span><span class="token plain"> calculator </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">SyncToolSpecification</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"calculator"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> inputSchema</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Basic calculator"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">callHandler</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">exchange</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">-&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">int</span><span class="token plain"> a </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">int</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">arguments</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"a"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">int</span><span class="token plain"> b </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">int</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">arguments</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"b"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token class-name">CallToolResult</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">content</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">List</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">of</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">McpSchema</span><span class="token class-name punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token class-name">TextContent</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Result: "</span><span class="token plain"> </span><span class="token operator">+</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">a </span><span class="token operator">+</span><span class="token plain"> b</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">server</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">addTool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">calculator</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>When exposing it as a remote HTTP service, switch the transport to the core built-in <code>HttpServletStreamableServerTransportProvider</code> (or the Spring WebMVC / WebFlux provider) and register the MCP endpoint at <code>/mcp</code>.</p>
<p><strong>4) Java comparison of the three transports</strong></p>
<table><thead><tr><th>Transport</th><th>Client class</th><th>Server class</th><th>Remarks</th></tr></thead><tbody><tr><td>STDIO</td><td><code>StdioClientTransport</code></td><td><code>StdioServerTransportProvider</code></td><td>Local subprocess</td></tr><tr><td>Streamable HTTP</td><td><code>HttpClientStreamableHttpTransport</code></td><td><code>HttpServletStreamableServerTransportProvider</code></td><td>Remote; Spring also has WebMVC/WebFlux</td></tr><tr><td>SSE (legacy)</td><td><code>HttpClientSseClientTransport</code></td><td><code>HttpServletSseServerTransportProvider</code></td><td>Deprecated, only for compatibility with older Servers</td></tr></tbody></table>
<p><strong>5) Higher-level one-click solutions</strong></p>
<ul>
<li class=""><strong>Spring AI</strong>: Boot Starter + <code>@McpTool</code> / <code>@McpToolParam</code> annotations + <code>SyncMcpToolCallback</code>, adapting MCP tools directly into Spring tool callbacks.</li>
<li class=""><strong>AgentScope Java</strong>: <code>McpClientBuilder</code> + <code>Toolkit.registerMcpClient(...)</code>, see Chapter 6.</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="58-choosing-an-implementation-in-the-java-ecosystem">5.8. Choosing an Implementation in the Java Ecosystem<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#58-choosing-an-implementation-in-the-java-ecosystem" class="hash-link" aria-label="Direct link to 5.8. Choosing an Implementation in the Java Ecosystem" title="Direct link to 5.8. Choosing an Implementation in the Java Ecosystem" translate="no">​</a></h3>
<table><thead><tr><th>Solution</th><th>Positioning</th><th>Connection</th><th>Tool adaptation</th><th>Suitable for</th></tr></thead><tbody><tr><td><strong>Official MCP Java SDK</strong></td><td>Protocol-layer reference implementation</td><td><code>McpClient</code> / <code>McpServer</code></td><td>Adapt yourself</td><td>Full control over protocol details</td></tr><tr><td><strong>Spring AI</strong></td><td>Spring ecosystem integration</td><td><code>McpSyncClient</code> / Starter</td><td><code>SyncMcpToolCallback</code> / <code>@McpTool</code></td><td>Spring Boot applications</td></tr><tr><td><strong>LangChain4j</strong></td><td>LLM application framework</td><td><code>McpTransport</code> + <code>DefaultMcpClient</code></td><td><code>McpToolExecutor</code> / <code>McpToolProvider</code></td><td>AI Services already using LangChain4j</td></tr><tr><td><strong>AgentScope Java</strong></td><td>Multi-Agent framework</td><td><code>McpClientBuilder</code></td><td><code>Toolkit.registerMcpClient</code></td><td>Multi-Agent orchestration, tool governance needed</td></tr></tbody></table>
<blockquote>
<p><strong>Key point</strong>: When choosing, don't look at "who is more complete," but at "which ecosystem your Agent runs in." Protocol behavior is guaranteed by the SDK; the differences lie mainly in tool governance, annotation experience, and orchestration capability.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-mcp-design-workflow-for-integrating-with-an-agent">6. MCP Design Workflow for Integrating with an Agent<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#6-mcp-design-workflow-for-integrating-with-an-agent" class="hash-link" aria-label="Direct link to 6. MCP Design Workflow for Integrating with an Agent" title="Direct link to 6. MCP Design Workflow for Integrating with an Agent" translate="no">​</a></h2>
<blockquote>
<p><strong>This section answers</strong>: When integrating one or more MCP Servers into an Agent, what workflow should be followed? Referring to the <strong>AgentScope (Java)</strong> implementation, it can be summarized as six steps: "Connect → Discover → Map → Execute → Lifecycle → Security."</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="61-overall-workflow">6.1. Overall Workflow<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#61-overall-workflow" class="hash-link" aria-label="Direct link to 6.1. Overall Workflow" title="Direct link to 6.1. Overall Workflow" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">① 连接 Connect       选传输、建 Client、初始化/能力协商、超时</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ↓</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">② 发现 Discover      tools/list（分页）→ 工具清单</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ↓</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">③ 映射 Map           MCP Tool → Agent 工具定义（inputSchema 透传 + 命名空间消歧）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ↓</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">④ 执行 Execute       Agent 工具循环 → tools/call → content/isError → 回填</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ↓</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">⑤ 生命周期 Lifecycle listChanged/subscriptions、连接复用、关闭/重启</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ↓</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">⑥ 安全 Governance     审批、白名单、Tool Poisoning、凭据</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="62-two-key-timings-pull-during-discovery-vs-call-during-execution">6.2. Two Key Timings: Pull During Discovery vs. Call During Execution<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#62-two-key-timings-pull-during-discovery-vs-call-during-execution" class="hash-link" aria-label="Direct link to 6.2. Two Key Timings: Pull During Discovery vs. Call During Execution" title="Direct link to 6.2. Two Key Timings: Pull During Discovery vs. Call During Execution" translate="no">​</a></h3>
<blockquote>
<p><strong>Core conclusion</strong>: <code>tools/list</code> (pulling tools) happens during <strong>Agent startup / registration</strong>, before entering the main loop; <code>tools/call</code> (invoking tools) happens during the <strong>Act phase of each ReAct round</strong>, after the model selects a tool. The two have different timings and frequencies.</p>
</blockquote>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">【启动 / 注册期 · 通常只做一次】</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  connect → initialize / server.discover（版本 + 能力协商）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     └─ tools/list ──► 消歧 · 过滤 · 分组 ──► 注册进 Agent 的“工具表”（缓存）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        （此表即后续每轮提供给模型的工具声明）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">【每次 run · 每轮对话循环】</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  loop {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    Think：组装请求（把缓存的工具声明序列化进 LLM 的 tools[]）→ 调 LLM</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">             ├─ 模型未选工具 → 结束本轮</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">             └─ 模型返回 tool_call(s)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    Act：  按 name 查“工具表” → tools/call ──► MCP Server 执行</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">             └─ content / structuredContent / isError 写回 memory → 回到 Think</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">【运行期 · 可选】</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  notifications/tools/list_changed / subscriptions/listen</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     └─ 重新 tools/list → 刷新工具表（一般安排在轮次之间，避免打断推理）</span><br></div></code></pre></div></div>
<p><strong>A few easily confused points:</strong></p>
<ol>
<li class=""><strong><code>tools/list</code> is not sent every round</strong>: after discovering once, it is cached; each round's model request uses this cache and does not repeatedly call the protocol.</li>
<li class=""><strong>"Giving tool declarations to the model" ≠ <code>tools/list</code></strong>: the Think phase merely serializes the cached declarations into the <code>tools[]</code> field of the LLM request, which is local data assembly.</li>
<li class=""><strong><code>tools/call</code> only happens when the model selects a tool</strong>: a single response may contain 0, 1, or multiple tool_calls; the Act phase executes them one by one (or in parallel).</li>
<li class=""><strong>Refresh timing</strong>: re-discovery is required only when the server declares <code>listChanged</code> (or subscribes via <code>subscriptions/listen</code>); refresh is scheduled between rounds to avoid changing the tool set mid-way and causing model context inconsistency.</li>
<li class=""><strong>Lazy discovery variant</strong>: a few frameworks pull only on first session / first use, or lazily activate subsets by group; but the skeleton of "discover first, execute later" remains unchanged.</li>
</ol>
<p><strong>Timing reference table:</strong></p>
<table><thead><tr><th>Action</th><th>Timing</th><th>Frequency</th><th>Trigger</th></tr></thead><tbody><tr><td><code>connect</code> / <code>initialize</code></td><td>Agent startup</td><td>Once</td><td>Agent initialization</td></tr><tr><td><code>tools/list</code> (pull)</td><td>Registration (before the loop)</td><td>Once; re-pull on <code>listChanged</code></td><td>Agent initialization / change notification</td></tr><tr><td>Tool declarations into the LLM request</td><td>Each Think round</td><td>Every round</td><td>Every model call</td></tr><tr><td><code>tools/call</code> (invoke)</td><td>Each Act round</td><td>When there is a tool_call</td><td>Model selection + tool loop</td></tr><tr><td><code>close</code></td><td>Agent destruction</td><td>Once</td><td>Lifecycle management</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="63-step-one-connect">6.3. Step One: Connect<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#63-step-one-connect" class="hash-link" aria-label="Direct link to 6.3. Step One: Connect" title="Direct link to 6.3. Step One: Connect" translate="no">​</a></h3>
<ul>
<li class=""><strong>Choose a transport</strong>: use stdio (subprocess) for local tools, Streamable HTTP for remote, and SSE only for backward compatibility with older servers.</li>
<li class=""><strong>Create a Client</strong>: declare capabilities (roots / sampling / elicitation), set request timeout and initialization timeout.</li>
<li class=""><strong>Version compatibility</strong>: the new specification uses per-request <code>_meta</code>; when compatible with older servers, perform dual-era detection (see 4.6). AgentScope explicitly declares supported versions via <code>protocolVersions(...)</code>:</li>
</ul>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">McpClientWrapper</span><span class="token plain"> client </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">McpClientBuilder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">create</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"filesystem-mcp"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">stdioTransport</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"npx"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"-y"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"@modelcontextprotocol/server-filesystem"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/tmp"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">timeout</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Duration</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">ofSeconds</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">120</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">initializationTimeout</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Duration</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">ofSeconds</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">30</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">protocolVersions</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"2024-11-05"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2025-03-26"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2025-06-18"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">buildAsync</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">block</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Note</strong>: AgentScope declares only <code>2024-11-05</code> by default; to connect to a newer Server you must explicitly call <code>protocolVersions(...)</code>, otherwise it reports "Unsupported protocol version". For the HTTP transport you can additionally add <code>.header("Authorization", "Bearer ...")</code> to inject credentials.</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="64-step-two-tool-discovery">6.4. Step Two: Tool Discovery<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#64-step-two-tool-discovery" class="hash-link" aria-label="Direct link to 6.4. Step Two: Tool Discovery" title="Direct link to 6.4. Step Two: Tool Discovery" translate="no">​</a></h3>
<ul>
<li class="">Use <code>tools/list</code> to pull the full set (<strong>pagination loop</strong>); when the server declares <code>listChanged</code>, you can subscribe to changes.</li>
<li class=""><strong>Namespace disambiguation</strong>: AgentScope uses <code>mcp__{server_name}__{tool_name}</code> to avoid duplicate names across multiple Servers (for example, two Servers both have <code>search</code>).</li>
<li class=""><strong>Filtering</strong>: allowlist / denylist (<code>enableTools</code> / <code>disableTools</code>) for minimal exposure.</li>
<li class=""><strong>Grouping</strong>: group by Server or purpose and activate on demand (tool groups) to reduce the number of tools pushed to the model at once.</li>
</ul>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">Toolkit</span><span class="token plain"> toolkit </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">Toolkit</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">toolkit</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">registerMcpClient</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">mcpClient</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">block</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">                 </span><span class="token comment" style="color:rgb(98, 114, 164)">// 全量注册</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// 或过滤 + 分组</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">toolkit</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">registration</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">mcpClient</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">mcpClient</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">enableTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">List</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">of</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"read_file"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"list_directory"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">disableTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">List</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">of</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"delete_file"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">group</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"filesystem"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">apply</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="65-step-three-mapping-to-agent-toolsessentially-converting-to-the-function-calling-protocol">6.5. Step Three: Mapping to Agent Tools—Essentially Converting to the Function Calling Protocol<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#65-step-three-mapping-to-agent-toolsessentially-converting-to-the-function-calling-protocol" class="hash-link" aria-label="Direct link to 6.5. Step Three: Mapping to Agent Tools—Essentially Converting to the Function Calling Protocol" title="Direct link to 6.5. Step Three: Mapping to Agent Tools—Essentially Converting to the Function Calling Protocol" translate="no">​</a></h3>
<blockquote>
<p><strong>Core conclusion</strong>: The essence of integrating MCP tools into an Agent is a <strong>protocol conversion</strong>—converting the tools pulled by MCP <code>tools/list</code> into the current model Provider's <strong>Function Calling protocol</strong> (OpenAI's <code>tools[].function</code> / Anthropic's <code>tools[]</code>), delivered to the model for selection with each LLM request; after the model returns a <code>tool_call</code>, it is <strong>reverse-mapped</strong> back to MCP <code>tools/call</code>. In one sentence: <strong>MCP governs "source and execution," while Function Calling governs "presentation and selection."</strong></p>
</blockquote>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">MCP Server ──tools/list──► 工具清单 Tool{name, description, inputSchema}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                 │  适配层：MCP → Function Calling</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                 ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                   模型 Function-Calling schema</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                   （OpenAI tools[].function / Anthropic tools[]）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                 │  随每次 LLM 请求下发</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                 ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                          LLM 选择 → tool_call</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                 │  适配层：反向解析 name + arguments</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                 ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">MCP Server ◄──tools/call── 原始工具名 + arguments</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                 │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    CallToolResult ──► tool result 消息 ──► 回灌 LLM</span><br></div></code></pre></div></div>
<p><strong>Forward mapping (MCP Tool → Function Calling):</strong></p>
<table><thead><tr><th>MCP Tool (source)</th><th>OpenAI</th><th>Anthropic</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>function.name</code></td><td><code>name</code></td></tr><tr><td><code>description</code></td><td><code>function.description</code></td><td><code>description</code></td></tr><tr><td><code>inputSchema</code> (JSON Schema 2020-12)</td><td><code>function.parameters</code></td><td><code>input_schema</code></td></tr><tr><td><code>outputSchema</code></td><td>Generally ignored (or used for strict)</td><td>Ignored</td></tr><tr><td><code>annotations</code> / <code>icons</code></td><td>Partially mapped</td><td>Partially mapped</td></tr></tbody></table>
<blockquote>
<p><code>inputSchema</code> is itself JSON Schema and can almost be passed through directly as <code>parameters</code> / <code>input_schema</code>—this is the key reason MCP and Function Calling can be integrated at low cost.</p>
</blockquote>
<p><strong>Reverse mapping (model tool_call → MCP <code>tools/call</code>):</strong></p>
<table><thead><tr><th>Model return</th><th>Handling</th></tr></thead><tbody><tr><td><code>name</code></td><td>Strip the disambiguation prefix → restore to the original MCP tool name</td></tr><tr><td><code>arguments</code> (OpenAI is a JSON <strong>string</strong>; Anthropic <code>input</code> is an <strong>object</strong>)</td><td>Normalize into a Map → <code>arguments</code> of <code>tools/call</code></td></tr><tr><td><code>id</code> / <code>tool_use_id</code></td><td>Retained to associate the result back to that call</td></tr></tbody></table>
<p><strong>Result backfill (MCP CallToolResult → model tool result):</strong></p>
<table><thead><tr><th>MCP return</th><th>Converted to</th></tr></thead><tbody><tr><td><code>content[]</code> (text / image / audio / resource)</td><td>The content of the ToolMessage / <code>tool_result</code> block</td></tr><tr><td><code>structuredContent</code></td><td>The structured output pipeline (or serialized into text)</td></tr><tr><td><code>isError = true</code></td><td>Mark the tool result as an error and hand it to the model for self-correction</td></tr></tbody></table>
<p><strong>A few pitfalls:</strong></p>
<ol>
<li class=""><strong>This is a format conversion, not protocol translation</strong>: each vendor's Function Calling field names differ, and <code>inputSchema → parameters / input_schema</code> is handled by the Provider Adapter.</li>
<li class=""><strong>Every LLM request must carry tool declarations</strong> (the Chat API is stateless), but <code>tools/list</code> is pulled only once and cached—don't confuse the two.</li>
<li class=""><strong>Names must be disambiguated</strong>: after aggregation across multiple Servers, the <code>name</code> seen by the model may differ from the original MCP <code>name</code>, so a mapping table must be maintained.</li>
<li class=""><strong>arguments forms vary</strong>: OpenAI uses a JSON string while Anthropic uses an object; normalize during mapping.</li>
<li class=""><strong>MCP is not responsible for "the model selecting tools"</strong>: selection relies on the model's Function Calling capability; MCP only guarantees "unified tool definitions + unified execution."</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="66-step-four-execution-and-backfill-execute">6.6. Step Four: Execution and Backfill (Execute)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#66-step-four-execution-and-backfill-execute" class="hash-link" aria-label="Direct link to 6.6. Step Four: Execution and Backfill (Execute)" title="Direct link to 6.6. Step Four: Execution and Backfill (Execute)" translate="no">​</a></h3>
<ul>
<li class="">After the Agent's ReAct/Tool loop selects a tool → issue <code>tools/call(name, arguments)</code>.</li>
<li class="">Result handling has three branches:<!-- -->
<ul>
<li class=""><code>content</code> (text / image / audio / resource) → concatenate into text and feed back to the model;</li>
<li class=""><code>structuredContent</code> → pass through to pipelines that support structured output;</li>
<li class=""><code>isError = true</code> → return to the model as a "<strong>tool execution error</strong>" for self-correction (<strong>do not</strong> throw an exception to interrupt the conversation);</li>
<li class="">JSON-RPC <code>error</code> (<strong>protocol error</strong>) → go through the framework's error handling pipeline.</li>
</ul>
</li>
</ul>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Agent tool loop</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   │ 选中工具 github__search_repos</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">MCP callTool("search_repos", args)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">result.content / structuredContent / isError</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">回填对话，模型继续下一轮</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="67-step-five-lifecycle-and-hot-updates-lifecycle">6.7. Step Five: Lifecycle and Hot Updates (Lifecycle)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#67-step-five-lifecycle-and-hot-updates-lifecycle" class="hash-link" aria-label="Direct link to 6.7. Step Five: Lifecycle and Hot Updates (Lifecycle)" title="Direct link to 6.7. Step Five: Lifecycle and Hot Updates (Lifecycle)" translate="no">​</a></h3>
<ul>
<li class=""><strong>Connection reuse</strong>: one Client's long connection serves the entire Agent lifecycle.</li>
<li class=""><strong>Hot updates</strong>: <code>notifications/tools/list_changed</code> → re-<code>tools/list</code>; 2026-07-28 uses <code>subscriptions/listen</code> to subscribe to changes.</li>
<li class=""><strong>Shutdown</strong>: stdio closes stdin → waits → force kills; HTTP closes the connection.</li>
<li class=""><strong>Fault tolerance</strong>: stdio subprocess crashes are automatically restarted and requests retried; HTTP timeouts are retried.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="68-step-six-security-and-governance">6.8. Step Six: Security and Governance<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#68-step-six-security-and-governance" class="hash-link" aria-label="Direct link to 6.8. Step Six: Security and Governance" title="Direct link to 6.8. Step Six: Security and Governance" translate="no">​</a></h3>
<ul>
<li class=""><strong>Human-in-the-loop</strong>: approval before sensitive tool invocations (AgentScope has a Permission / Hook system; other frameworks can implement it with middleware).</li>
<li class=""><strong>Minimal exposure</strong>: register only the necessary tools (allowlist first).</li>
<li class=""><strong>Tool Poisoning</strong>: treat tool descriptions and annotations as untrusted input at all times.</li>
<li class=""><strong>Credentials</strong>: the HTTP transport uses OAuth / API Key (<code>header(...)</code>); stdio uses environment variables.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="69-reference-implementation-comparison">6.9. Reference Implementation Comparison<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#69-reference-implementation-comparison" class="hash-link" aria-label="Direct link to 6.9. Reference Implementation Comparison" title="Direct link to 6.9. Reference Implementation Comparison" translate="no">​</a></h3>
<table><thead><tr><th>Framework</th><th>Connection</th><th>Discovery / Registration</th><th>Execution adaptation</th><th>Features</th></tr></thead><tbody><tr><td><strong>AgentScope Java</strong></td><td><code>McpClientBuilder</code> (StdIO / SSE / StreamableHTTP)</td><td><code>Toolkit.registerMcpClient</code></td><td>Unified Toolkit tool table</td><td>Tool filtering / grouping, Higress gateway semantic retrieval, elicitation callbacks</td></tr><tr><td><strong>LangChain4j</strong></td><td><code>McpTransport</code> + <code>DefaultMcpClient</code></td><td><code>McpToolProvider</code></td><td><code>McpToolExecutor</code></td><td>Aligned with AiServices / ToolProvider</td></tr><tr><td><strong>Spring AI</strong></td><td><code>McpSyncClient</code> / <code>McpAsyncClient</code></td><td><code>SyncMcpToolCallbackProvider</code></td><td><code>SyncMcpToolCallback</code></td><td>Boot Starter, <code>@McpTool</code> annotations, Security integration</td></tr></tbody></table>
<blockquote>
<p><strong>Key point</strong>: No matter which framework is used, the skeleton for an Agent to integrate MCP is the same—"<strong>Client connects → tools/list discovers → tool adaptation → tools/call executes → lifecycle and security governance</strong>." The difference lies only in API form and governance capability.</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="610-the-complete-underlying-call-process-frame-by-frame">6.10. The Complete Underlying Call Process (Frame-by-Frame)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#610-the-complete-underlying-call-process-frame-by-frame" class="hash-link" aria-label="Direct link to 6.10. The Complete Underlying Call Process (Frame-by-Frame)" title="Direct link to 6.10. The Complete Underlying Call Process (Frame-by-Frame)" translate="no">​</a></h3>
<blockquote>
<p>This section breaks down "MCP integration into an Agent" to the <strong>protocol frame level</strong>: from the Agent starting the connection, to tool discovery, format conversion, model selection, tool execution, result backfill, loop convergence, refresh, and shutdown, each step gives the real JSON / invocation fragments. The examples uniformly use: Server alias <code>weather</code>, MCP original tool name <code>get_weather</code>, tool name exposed to the model <code>weather__get_weather</code>.</p>
</blockquote>
<p><strong>Panoramic sequence (one complete run):</strong></p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">[启动期]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  0. 装配 MCP Client（传输 + 能力 + 超时）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  1. 建立连接（stdio 起子进程 / HTTP 建连接）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  2. 初始化：server/discover（现代）或 initialize（传统）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  3. tools/list（分页拉全量）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  4. 消歧/过滤/分组 → 写入 ToolRegistry，并建映射表</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        llmName(weather__get_weather) ⇄ (server=weather, mcpName=get_weather, executor)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[每次 run]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  loop {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    5. Think：组装 ChatRequest（tools = 缓存的声明转 provider 格式）→ 调 LLM</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    6. LLM 返回：text 或 tool_calls[]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    7. 若有 tool_calls → Act：</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">         a. 用 llmName 查映射表得到 (mcpName, executor)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">         b. arguments 归一化成 Map</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">         c. before-tool 中间件（审批/日志）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">         d. executor → tools/call（JSON-RPC）→ MCP Server 执行</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">         e. result(content/structuredContent/isError) → tool result 消息（关联 call id）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">         f. after-tool 中间件</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">         g. 写回 memory</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    8. 回到 5（带 tool results）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    9. LLM 不再返回 tool_calls → 输出最终答案 → run 结束</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[运行期]    10. list_changed / subscriptions → 重新 tools/list → 刷新注册表</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[销毁期]    11. close：stdio 关 stdin→等待→强杀；HTTP 关闭连接</span><br></div></code></pre></div></div>
<br>
<p><strong>Frame 0-1: Assembly and connection</strong></p>
<ul>
<li class="">Choose transport: stdio for local (<code>ProcessBuilder</code> starts a subprocess), Streamable HTTP for remote.</li>
<li class="">Assemble the Client: declare capabilities (roots / sampling / elicitation), request timeout, initialization timeout.</li>
</ul>
<br>
<p><strong>Frame 2: Initialization (modern <code>server/discover</code>)</strong></p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// → 请求（stdio：一整行 JSON）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"discover-1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"method"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"server/discover"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"params"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"_meta"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"io.modelcontextprotocol/protocolVersion"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2026-07-28"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"io.modelcontextprotocol/clientInfo"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"MyAgent"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"version"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"1.0.0"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"io.modelcontextprotocol/clientCapabilities"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// ← 响应</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"discover-1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"result"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"resultType"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"complete"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"supportedVersions"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"2026-07-28"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"capabilities"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"tools"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"listChanged"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"_meta"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"io.modelcontextprotocol/serverInfo"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"weather-server"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"version"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2.1.0"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Legacy version (≤ 2025-11-25)</strong>: first negotiate version and capabilities with the <code>initialize</code> request/response, then add a <code>notifications/initialized</code>. The modern version has no handshake; capabilities are carried in the <code>_meta</code> of each request.</p>
</blockquote>
<br>
<p><strong>Frame 3: Tool discovery (<code>tools/list</code>)</strong></p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// → 请求</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"method"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tools/list"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"params"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"_meta"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"io.modelcontextprotocol/protocolVersion"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2026-07-28"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"io.modelcontextprotocol/clientCapabilities"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// ← 响应</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"result"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"resultType"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"complete"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"tools"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"description"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Get current weather for a city"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"inputSchema"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"object"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token property">"properties"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"location"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"string"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"description"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"City name"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token property">"required"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"location"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"nextCursor"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token null keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"ttlMs"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">300000</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"cacheScope"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"public"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Note</strong>: When <code>nextCursor</code> is non-null, continue pulling the next page; <code>ttlMs</code> / <code>cacheScope</code> are cache hints. After pulling, enter the registration stage.</p>
</blockquote>
<br>
<p><strong>Frame 4: Registration and mapping (local in-memory structures)</strong></p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">ToolRegistry（缓存，供每轮 Think 复用）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">┌────────────────────────┬──────────┬──────────────┬─────────────────────────┐</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│ llmName                │ server   │ mcpName      │ executor                │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├────────────────────────┼──────────┼──────────────┼─────────────────────────┤</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│ weather__get_weather   │ weather  │ get_weather  │ McpToolExecutor(#1)     │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">└────────────────────────┴──────────┴──────────────┴─────────────────────────┘</span><br></div></code></pre></div></div>
<ul>
<li class=""><code>llmName</code>: the name exposed to the model after adding the Server prefix for disambiguation.</li>
<li class=""><code>mcpName</code>: the original tool name on the MCP Server, used in <code>tools/call</code>.</li>
<li class=""><code>executor</code>: the executor bound to that Server Client.</li>
</ul>
<br>
<p><strong>Frame 5: Think (assemble the LLM request, convert to Function Calling)</strong></p>
<p>Convert the declarations in the registry into the Provider's Function Calling format and deliver them with the request (<strong>this step is not <code>tools/list</code>, it is local serialization</strong>):</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"model"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"gpt-x"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"messages"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"system"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"You are a helpful assistant."</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"user"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"杭州今天天气怎么样？"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"tools"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"function"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"function"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"weather__get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"description"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Get current weather for a city"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"parameters"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"object"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token property">"properties"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"location"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"string"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"description"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"City name"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token property">"required"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"location"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<br>
<p><strong>Frame 6: LLM selects a tool (returns tool_call)</strong></p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"chatcmpl-1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"choices"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"finish_reason"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tool_calls"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token null keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"tool_calls"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"call_abc"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"function"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token property">"function"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"weather__get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"arguments"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"{\"location\":\"Hangzhou\"}"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<br>
<p><strong>Frame 7: Act (reverse mapping → <code>tools/call</code>)</strong></p>
<ul>
<li class="">Look up the table using <code>llmName = weather__get_weather</code> → <code>(server=weather, mcpName=get_weather, executor)</code>.</li>
<li class="">Parse <code>arguments</code> (OpenAI is a JSON string) → <code>{"location":"Hangzhou"}</code>.</li>
<li class="">Trigger the before-tool middleware (approval / logging / instrumentation).</li>
</ul>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// → tools/call</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">3</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"method"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tools/call"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"params"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"arguments"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"location"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Hangzhou"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"_meta"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"io.modelcontextprotocol/protocolVersion"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2026-07-28"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"io.modelcontextprotocol/clientCapabilities"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// ← 响应</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">3</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"result"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"resultType"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"complete"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"text"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"text"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Hangzhou: 26°C, sunny"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"structuredContent"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"tempC"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">26</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"condition"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"sunny"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"isError"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">false</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p>Under the HTTP transport, the same request also carries the headers: <code>MCP-Protocol-Version</code>, <code>Mcp-Method: tools/call</code>, <code>Mcp-Name: get_weather</code>.</p>
</blockquote>
<br>
<p><strong>Frame 8: Result backfill (MCP result → tool result message)</strong></p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tool"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"tool_call_id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"call_abc"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Hangzhou: 26°C, sunny"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<ul>
<li class=""><code>content[]</code> is concatenated into text; <code>structuredContent</code> can go through the structured pipeline.</li>
<li class=""><code>isError=true</code> → returned to the model as a "tool execution error" for self-correction; JSON-RPC <code>error</code> → goes through the protocol error pipeline.</li>
<li class="">Trigger the after-tool middleware, and after writing to memory, return to Frame 5.</li>
</ul>
<br>
<p><strong>Frame 9: Think again and converge</strong></p>
<p>The second LLM request carries the tool result; the model no longer returns <code>tool_calls</code>, directly outputs the final answer, and the run ends. If a single response contains multiple <code>tool_call</code>s, execute Frames 7-8 one by one (or in parallel), then merge and feed back.</p>
<br>
<p><strong>Frame 10-11: Runtime refresh and shutdown</strong></p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">运行期（可选）：</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  ← notifications/tools/list_changed            （Server 声明 listChanged 时）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  → tools/list（重新拉取）→ 更新 ToolRegistry（安排在轮次之间）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">销毁期：</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  stdio：关闭 stdin → 等待进程退出 → 超时 SIGTERM → 再超时 SIGKILL</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  HTTP ：关闭连接 / 连接池</span><br></div></code></pre></div></div>
<br>
<p><strong>Underlying state and data structures:</strong></p>
<table><thead><tr><th>Structure</th><th>Role</th></tr></thead><tbody><tr><td><code>pending: id → Future</code></td><td>JSON-RPC request/response correlation</td></tr><tr><td><code>ToolRegistry</code></td><td>llmName → (server, mcpName, executor, schema) mapping and cache</td></tr><tr><td><code>messages / memory</code></td><td>system + history + tool result, accumulated round by round</td></tr><tr><td><code>capabilities</code></td><td>Both parties' capabilities, determining available features (tools/resources/sampling...)</td></tr><tr><td><code>protocolVersions</code></td><td>The set of supported versions, used for negotiation and fallback</td></tr></tbody></table>
<p><strong>Branches and exceptions:</strong></p>
<ul>
<li class=""><strong>Protocol error vs tool execution error</strong>: the former goes through JSON-RPC <code>error</code>, the latter through <code>result.isError=true</code>; the latter should be returned to the model for self-correction.</li>
<li class=""><strong>Version mismatch</strong>: <code>UnsupportedProtocolVersionError</code> (<code>-32022</code>) → pick a version from <code>supported</code> and retry; old Server → fall back to <code>initialize</code>.</li>
<li class=""><strong>Timeout / cancellation</strong>: the client enforces timeouts; stdio sends <code>notifications/cancelled</code>, HTTP closes the response stream.</li>
<li class=""><strong>Parallel tool_calls</strong>: one round can contain multiple calls; they must be fed back separately by <code>id</code>, and order does not affect correlation.</li>
<li class=""><strong>List changes</strong>: <code>list_changed</code> triggers a re-pull, refreshing the registry and mapping.</li>
</ul>
<br>
<p><strong>Three-party sequence diagram (Agent/Host ↔ MCP Client ↔ MCP Server ↔ LLM):</strong></p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">sequenceDiagram</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    participant H as Agent/Host</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    participant C as MCP Client</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    participant S as MCP Server</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    participant M as LLM</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    Note over H,C: 启动期（一次）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    C-&gt;&gt;S: server/discover（或 initialize）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    S--&gt;&gt;C: supportedVersions + capabilities</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    C-&gt;&gt;S: tools/list</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    S--&gt;&gt;C: tools[]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    Note over H: 消歧/过滤/分组 → ToolRegistry</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    loop 每轮对话</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        H-&gt;&gt;M: ChatRequest（messages + tools（Function Calling 格式））</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        M--&gt;&gt;H: tool_calls[]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        H-&gt;&gt;C: 反向映射（llmName → server + mcpName）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        C-&gt;&gt;S: tools/call(name, arguments)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        S--&gt;&gt;C: content / structuredContent / isError</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        H-&gt;&gt;M: tool result（关联 call id）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        M--&gt;&gt;H: 最终答案 或 继续调用工具</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    Note over C,S: 运行期（可选）list_changed → 重新 tools/list</span><br></div></code></pre></div></div>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="supplementary-frame-a-legacy-initialization--2025-11-25">Supplementary Frame A: Legacy Initialization (≤ 2025-11-25)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#supplementary-frame-a-legacy-initialization--2025-11-25" class="hash-link" aria-label="Direct link to Supplementary Frame A: Legacy Initialization (≤ 2025-11-25)" title="Direct link to Supplementary Frame A: Legacy Initialization (≤ 2025-11-25)" translate="no">​</a></h4>
<p>The modern version has no handshake; when connecting to an old Server, it goes through the <code>initialize</code> handshake:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// → initialize</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"method"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"initialize"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"params"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"protocolVersion"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2025-06-18"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">              </span><span class="token property">"capabilities"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"roots"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"listChanged"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">              </span><span class="token property">"clientInfo"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"MyAgent"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"version"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"1.0.0"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// ← 响应</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"result"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"protocolVersion"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2025-06-18"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">              </span><span class="token property">"capabilities"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"tools"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"listChanged"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">              </span><span class="token property">"serverInfo"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"weather-server"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"version"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2.1.0"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// → 客户端就绪通知（无 id）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"method"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"notifications/initialized"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="supplementary-frame-b-http--sse-streaming-response-long-running-tools">Supplementary Frame B: HTTP + SSE Streaming Response (Long-Running Tools)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#supplementary-frame-b-http--sse-streaming-response-long-running-tools" class="hash-link" aria-label="Direct link to Supplementary Frame B: HTTP + SSE Streaming Response (Long-Running Tools)" title="Direct link to Supplementary Frame B: HTTP + SSE Streaming Response (Long-Running Tools)" translate="no">​</a></h4>
<p>Under Streamable HTTP, the client POSTs a single request; the server may return a <strong>request-level SSE stream</strong>, pushing progress first, with the last entry being the final response:</p>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">POST /mcp HTTP/1.1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Content-Type: application/json</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Accept: application/json, text/event-stream</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">MCP-Protocol-Version: 2026-07-28</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Mcp-Method: tools/call</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Mcp-Name: get_weather</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">{ "jsonrpc": "2.0", "id": 3, "method": "tools/call",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  "params": { "name": "get_weather", "arguments": { "location": "Hangzhou" },</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    "_meta": { "io.modelcontextprotocol/protocolVersion": "2026-07-28",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">               "io.modelcontextprotocol/clientCapabilities": {} } } }</span><br></div></code></pre></div></div>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">HTTP/1.1 200 OK</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Content-Type: text/event-stream</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">X-Accel-Buffering: no</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: message</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"jsonrpc":"2.0","method":"notifications/progress","params":{"progressToken":"p1","progress":1,"total":3}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: message</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"jsonrpc":"2.0","method":"notifications/progress","params":{"progressToken":"p1","progress":2,"total":3}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: message</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"jsonrpc":"2.0","id":3,"result":{"resultType":"complete","content":[{"type":"text","text":"Hangzhou: 26°C, sunny"}],"isError":false}}</span><br></div></code></pre></div></div>
<blockquote>
<p>The last SSE event is the <strong>final response</strong>, after which the stream is closed. Request-level notifications (<code>notifications/progress</code>) appear only on that request's response stream.</p>
</blockquote>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="supplementary-frame-c-parallel-multi-tool-invocation">Supplementary Frame C: Parallel Multi-Tool Invocation<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#supplementary-frame-c-parallel-multi-tool-invocation" class="hash-link" aria-label="Direct link to Supplementary Frame C: Parallel Multi-Tool Invocation" title="Direct link to Supplementary Frame C: Parallel Multi-Tool Invocation" translate="no">​</a></h4>
<p>One round of LLM response can return multiple <code>tool_call</code>s; the Agent executes them one by one (or in parallel), then correlates multiple tool results back by <code>id</code>:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// ← LLM 返回两个工具调用</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"tool_calls"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"call_a"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"function"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"function"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"weather__get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"arguments"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"{\"location\":\"Hangzhou\"}"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"call_b"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"function"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"function"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"time__now"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"arguments"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"{}"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// → 两次 tools/call（可并行），id 分别为 3、4</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">3</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"method"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tools/call"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"params"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"arguments"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"location"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Hangzhou"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">4</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"method"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tools/call"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"params"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"now"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"arguments"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// ← 两条 tool result，按 call id 关联</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tool"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"tool_call_id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"call_a"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Hangzhou: 26°C, sunny"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tool"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"tool_call_id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"call_b"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2026-10-05T10:00:00+08:00"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="supplementary-frame-d-mrtr-a-tool-needs-additional-input">Supplementary Frame D: MRTR (A Tool Needs Additional Input)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#supplementary-frame-d-mrtr-a-tool-needs-additional-input" class="hash-link" aria-label="Direct link to Supplementary Frame D: MRTR (A Tool Needs Additional Input)" title="Direct link to Supplementary Frame D: MRTR (A Tool Needs Additional Input)" translate="no">​</a></h4>
<p>When the Server needs the client to supply additional input, it no longer proactively sends a request; instead it returns <code>resultType: "input_required"</code>; after the client fills it in, it replays with a <strong>new id</strong>:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// ← 第一次 tools/call 的中间结果</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">3</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"result"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"resultType"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"input_required"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"inputRequests"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"login"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"method"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"elicitation/create"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"params"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"GitHub username?"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"mode"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"form"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"requestedSchema"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"object"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"properties"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"string"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"required"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"name"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"requestState"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"eyJsb2NhdGlvbiI6IkhhbmcgemhvdSJ9"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// → 用新 id=4 重放，并带上 inputResponses + requestState</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"jsonrpc"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">4</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"method"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tools/call"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"params"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"arguments"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"location"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Hangzhou"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"inputResponses"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"login"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"action"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"accept"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"octocat"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"requestState"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"eyJsb2NhdGlvbiI6IkhhbmcgemhvdSJ9"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="supplementary-frame-e-two-types-of-error-frames-must-be-distinguished">Supplementary Frame E: Two Types of Error Frames (Must Be Distinguished)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#supplementary-frame-e-two-types-of-error-frames-must-be-distinguished" class="hash-link" aria-label="Direct link to Supplementary Frame E: Two Types of Error Frames (Must Be Distinguished)" title="Direct link to Supplementary Frame E: Two Types of Error Frames (Must Be Distinguished)" translate="no">​</a></h4>
<div class="language-jsonc codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-jsonc codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">// 工具执行错误：走 result.isError —— 回给模型自纠</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">{ "jsonrpc": "2.0", "id": 3, "result": { "resultType": "complete",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  "content": [{ "type": "text", "text": "Invalid location: no such city." }], "isError": true } }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">// 协议错误：走 JSON-RPC error —— 交给框架错误处理</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">{ "jsonrpc": "2.0", "id": 3, "error": { "code": -32602, "message": "Unknown tool: get_weather_x" } }</span><br></div></code></pre></div></div>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="supplementary-frame-f-cancellation-and-timeout">Supplementary Frame F: Cancellation and Timeout<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#supplementary-frame-f-cancellation-and-timeout" class="hash-link" aria-label="Direct link to Supplementary Frame F: Cancellation and Timeout" title="Direct link to Supplementary Frame F: Cancellation and Timeout" translate="no">​</a></h4>
<div class="language-jsonc codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-jsonc codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">// stdio：客户端发取消通知（引用请求 id）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">{ "jsonrpc": "2.0", "method": "notifications/cancelled", "params": { "requestId": 3, "reason": "user aborted" } }</span><br></div></code></pre></div></div>
<blockquote>
<p>The HTTP transport has no cancellation notification: <strong>closing that request's response stream</strong> is considered cancellation (2026-07-28 removes SSE recoverability; a broken stream means the request is lost and must be resent with a new id).</p>
</blockquote>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="supplementary-frame-g-paginated-pulling">Supplementary Frame G: Paginated Pulling<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#supplementary-frame-g-paginated-pulling" class="hash-link" aria-label="Direct link to Supplementary Frame G: Paginated Pulling" title="Direct link to Supplementary Frame G: Paginated Pulling" translate="no">​</a></h4>
<div class="language-jsonc codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-jsonc codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">// 第一页</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">{ "jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {} }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">// ← result 中 nextCursor = "c1"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">// 第二页</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">{ "jsonrpc": "2.0", "id": 5, "method": "tools/list", "params": { "cursor": "c1" } }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">// ← result 中 nextCursor = null → 结束</span><br></div></code></pre></div></div>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="internal-mechanism-pending-routing-and-timeouts">Internal Mechanism: <code>pending</code> Routing and Timeouts<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#internal-mechanism-pending-routing-and-timeouts" class="hash-link" aria-label="Direct link to internal-mechanism-pending-routing-and-timeouts" title="Direct link to internal-mechanism-pending-routing-and-timeouts" translate="no">​</a></h4>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">pending: Map&lt;id, Future&gt;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  发送 tools/call(id=3)  → pending["3"] = F3</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  收到带 id=3 的响应     → pending.remove("3").complete(result)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  超时                   → F3 失败；pending.remove("3")（stdio 另发 notifications/cancelled）</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: The protocol is asynchronous, and <code>id</code> is the only correlation key; the client must add a timeout to the <code>Future</code> (receiving <code>notifications/progress</code> can reset the clock, but a maximum timeout must still be kept).</p>
</blockquote>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="complete-one-round-of-protocol-frame-pipeline-condensed-overview">Complete One Round of "Protocol Frame Pipeline" (Condensed Overview)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#complete-one-round-of-protocol-frame-pipeline-condensed-overview" class="hash-link" aria-label="Direct link to Complete One Round of &quot;Protocol Frame Pipeline&quot; (Condensed Overview)" title="Direct link to Complete One Round of &quot;Protocol Frame Pipeline&quot; (Condensed Overview)" translate="no">​</a></h4>
<table><thead><tr><th>No.</th><th>Direction</th><th>Frame / Action</th><th>Key fields</th></tr></thead><tbody><tr><td>1</td><td>C→S</td><td><code>server/discover</code></td><td><code>_meta.protocolVersion</code> / <code>clientCapabilities</code></td></tr><tr><td>2</td><td>S→C</td><td>discover result</td><td><code>supportedVersions</code> / <code>capabilities</code></td></tr><tr><td>3</td><td>C→S</td><td><code>tools/list</code></td><td><code>cursor</code></td></tr><tr><td>4</td><td>S→C</td><td>Tool list</td><td><code>tools[].inputSchema</code> / <code>nextCursor</code></td></tr><tr><td>5</td><td>Local</td><td>Register + map + filter/group</td><td><code>llmName ⇄ (server, mcpName)</code></td></tr><tr><td>6</td><td>H→M</td><td>ChatRequest</td><td><code>tools[]</code> (Function Calling format)</td></tr><tr><td>7</td><td>M→H</td><td><code>tool_calls[]</code></td><td><code>id</code> / <code>name</code> / <code>arguments</code></td></tr><tr><td>8</td><td>C→S</td><td><code>tools/call</code></td><td><code>name</code> / <code>arguments</code> / <code>Mcp-Name</code> header</td></tr><tr><td>9</td><td>S→C</td><td>Result</td><td><code>content</code> / <code>structuredContent</code> / <code>isError</code></td></tr><tr><td>10</td><td>H→M</td><td>tool result</td><td><code>tool_call_id</code> / <code>content</code></td></tr><tr><td>11</td><td>M→H</td><td>Final answer</td><td>No <code>tool_calls</code> → end</td></tr><tr><td>12</td><td>S→C</td><td>(runtime) <code>list_changed</code></td><td>Triggers re-pull of <code>tools/list</code></td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="611-design-checklist">6.11. Design Checklist<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#611-design-checklist" class="hash-link" aria-label="Direct link to 6.11. Design Checklist" title="Direct link to 6.11. Design Checklist" translate="no">​</a></h3>
<ul class="contains-task-list containsTaskList_mC6p">
<li class="task-list-item"><input type="checkbox" disabled=""> <!-- -->Does the transport match the deployment form (local stdio / remote HTTP)?</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <!-- -->Is protocol version compatibility (dual-era) handled?</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <!-- -->Are the three types of timeouts set (request / initialization / idle)?</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <!-- -->Is namespace disambiguation done for tool names?</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <!-- -->Is a tool allowlist / minimal exposure done?</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <!-- -->Is <code>isError</code> correctly returned to the model for self-correction?</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <!-- -->Is the tool list change monitored and hot-updated?</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <!-- -->Is there human approval and an audit log?</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <!-- -->Are connections correctly closed / restarted along with the Agent lifecycle?</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-security-and-authorization-design">7. Security and Authorization Design<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#7-security-and-authorization-design" class="hash-link" aria-label="Direct link to 7. Security and Authorization Design" title="Direct link to 7. Security and Authorization Design" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="71-authorization-evolution-spec">7.1. Authorization Evolution (<a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization" target="_blank" rel="noopener noreferrer" class="">spec</a>)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#71-authorization-evolution-spec" class="hash-link" aria-label="Direct link to 71-authorization-evolution-spec" title="Direct link to 71-authorization-evolution-spec" translate="no">​</a></h3>
<ul>
<li class=""><strong>2025-03-26</strong>: Introduces the <strong>OAuth 2.1</strong> authorization framework;</li>
<li class=""><strong>2025-06-18</strong>: Positions MCP Server as an <strong>OAuth Resource Server</strong> and requires <strong>Resource Indicators (RFC 8707)</strong> to prevent Token reuse;</li>
<li class=""><strong>2025-11-25</strong>: Supports <strong>OpenID Connect Discovery 1.0</strong>, incremental authorization scope consent (<code>WWW-Authenticate</code>), and recommends <strong>Client ID Metadata Documents</strong>;</li>
<li class=""><strong>2026-07-28</strong>: Further aligns with OAuth 2.0 / OIDC deployment practices, <strong>deprecates dynamic client registration (RFC 7591)</strong>, promotes Client ID Metadata Documents instead; requires validating <code>iss</code> (RFC 9207) and isolating credentials by issuer.</li>
</ul>
<blockquote>
<p><strong>Note</strong>: Authorization applies only to <strong>HTTP-type transports</strong>; the stdio transport <strong>SHOULD NOT</strong> use this framework, and should obtain credentials from environment variables instead.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="72-transport-security">7.2. Transport Security<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#72-transport-security" class="hash-link" aria-label="Direct link to 7.2. Transport Security" title="Direct link to 7.2. Transport Security" translate="no">​</a></h3>
<p>Hard requirements of Streamable HTTP:</p>
<ol>
<li class="">All inbound connections <strong>MUST</strong> validate <code>Origin</code>, returning <code>403</code> if invalid (to prevent DNS rebinding);</li>
<li class="">When running locally, <strong>SHOULD</strong> bind only to <code>127.0.0.1</code>, not <code>0.0.0.0</code>;</li>
<li class=""><strong>SHOULD</strong> implement authentication;</li>
<li class="">The server <strong>MUST</strong> validate that mirrored headers are consistent with the request body (return <code>400</code> + <code>HeaderMismatch</code> if inconsistent).</li>
</ol>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="73-tool-security">7.3. Tool Security<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#73-tool-security" class="hash-link" aria-label="Direct link to 7.3. Tool Security" title="Direct link to 7.3. Tool Security" translate="no">​</a></h3>
<ul>
<li class=""><strong>Human-in-the-loop</strong>: tools are model-controlled, and the ability for humans to refuse invocation <strong>SHOULD</strong> always be retained; the application should clearly display which tools are exposed, provide visual cues at invocation time, and pop up a confirmation for sensitive operations;</li>
<li class=""><strong>Tool Poisoning</strong>: tool annotations/descriptions must be treated as <strong>untrusted input</strong>;</li>
<li class=""><strong>Input/output security</strong>: the Server <strong>MUST</strong> validate all input, perform access control, rate limiting, and sanitize output; the Client <strong>SHOULD</strong> validate results before passing them to the LLM, set timeouts, and record audits;</li>
<li class=""><strong><code>x-mcp-header</code></strong>: only for routing; must not mark sensitive fields;</li>
<li class=""><strong>Icon security</strong>: icon URIs are allowed only for HTTPS or <code>data:</code>, rejecting <code>javascript:</code>/<code>file:</code> etc.; do not fetch with credentials; validate MIME and content (to prevent SVG-embedded scripts).</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-extension-mechanism-and-governance">8. Extension Mechanism and Governance<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#8-extension-mechanism-and-governance" class="hash-link" aria-label="Direct link to 8. Extension Mechanism and Governance" title="Direct link to 8. Extension Mechanism and Governance" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="81-extension-framework">8.1. Extension Framework<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#81-extension-framework" class="hash-link" aria-label="Direct link to 8.1. Extension Framework" title="Direct link to 8.1. Extension Framework" translate="no">​</a></h3>
<p>In 2026-07-28, an <code>extensions</code> field is added to <code>ClientCapabilities</code> / <code>ServerCapabilities</code>:</p>
<ul>
<li class="">Identifiers use a <strong>reverse-DNS prefix</strong> (e.g., <code>io.modelcontextprotocol/ui</code>);</li>
<li class="">Each extension comes with its own settings object and independent version;</li>
<li class="">When one party supports it and the other does not, the supporting party <strong>MUST</strong> fall back to core behavior or explicitly refuse.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="82-mcp-apps">8.2. MCP Apps<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#82-mcp-apps" class="hash-link" aria-label="Direct link to 8.2. MCP Apps" title="Direct link to 8.2. MCP Apps" translate="no">​</a></h3>
<p>Released on 2026-01-26 as the <strong>first official extension</strong>, allowing Servers to render interactive UI (tables, forms, etc.) in a sandboxed iframe, identified as <code>io.modelcontextprotocol/ui</code>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="83-tasks-extension">8.3. Tasks Extension<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#83-tasks-extension" class="hash-link" aria-label="Direct link to 8.3. Tasks Extension" title="Direct link to 8.3. Tasks Extension" translate="no">​</a></h3>
<p>Moves experimental tasks out of the core protocol into the official extension <code>io.modelcontextprotocol/tasks</code>, using <code>tasks/get</code> polling + <code>tasks/update</code> to supply input, replacing the blocking <code>tasks/result</code>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="84-deprecation-policy">8.4. Deprecation Policy<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#84-deprecation-policy" class="hash-link" aria-label="Direct link to 8.4. Deprecation Policy" title="Direct link to 8.4. Deprecation Policy" translate="no">​</a></h3>
<p>2026-07-28 establishes a formal <strong>feature lifecycle and deprecation policy</strong>: three states Active / Deprecated / Removed, a deprecation window of at least 12 months, and maintains a <a href="https://modelcontextprotocol.io/specification/2026-07-28/deprecated" target="_blank" rel="noopener noreferrer" class="">list of deprecated features</a>. Currently deprecated:</p>
<ul>
<li class=""><strong>Roots, Sampling, Logging</strong> (migration directions: tool parameters/resource URIs/passing directories via config; use the Provider API directly; <code>stderr</code> or OpenTelemetry);</li>
<li class=""><strong>HTTP+SSE transport</strong> (migrate to Streamable HTTP);</li>
<li class=""><strong><code>includeContext</code>'s <code>"thisServer"</code>/<code>"allServers"</code></strong>;</li>
<li class=""><strong>OAuth Dynamic Client Registration</strong>.</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="9-design-trade-offs-and-comparisons">9. Design Trade-offs and Comparisons<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#9-design-trade-offs-and-comparisons" class="hash-link" aria-label="Direct link to 9. Design Trade-offs and Comparisons" title="Direct link to 9. Design Trade-offs and Comparisons" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="91-transport-design-trade-offs-stdio-vs-streamable-http">9.1. Transport Design Trade-offs: stdio vs Streamable HTTP<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#91-transport-design-trade-offs-stdio-vs-streamable-http" class="hash-link" aria-label="Direct link to 9.1. Transport Design Trade-offs: stdio vs Streamable HTTP" title="Direct link to 9.1. Transport Design Trade-offs: stdio vs Streamable HTTP" translate="no">​</a></h3>
<table><thead><tr><th>Dimension</th><th>stdio</th><th>Streamable HTTP</th></tr></thead><tbody><tr><td>Deployment</td><td>Local subprocess</td><td>Remote service</td></tr><tr><td>Framing</td><td>Newline-delimited JSON</td><td>HTTP POST, response is JSON or SSE</td></tr><tr><td>State</td><td>None (2026-07-28)</td><td>None (session removed in 2026-07-28)</td></tr><tr><td>Authentication</td><td>Environment variables</td><td>OAuth 2.1 / OIDC</td></tr><tr><td>Cancellation</td><td><code>notifications/cancelled</code></td><td>Close the response stream</td></tr><tr><td>Suitable for</td><td>Local tools, desktop hosts</td><td>Cloud deployment, multi-tenancy, load balancing</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="92-stateful-vs-stateless">9.2. Stateful vs Stateless<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#92-stateful-vs-stateless" class="hash-link" aria-label="Direct link to 9.2. Stateful vs Stateless" title="Direct link to 9.2. Stateful vs Stateless" translate="no">​</a></h3>
<p>The old version used a session to provide "in-connection context," which was simple to implement but conflicted with horizontal scaling and load balancing; 2026-07-28 chooses <strong>stateless</strong>, making state explicit (a handle as an ordinary argument) in exchange for cloud-native friendliness. The cost is that each request must carry metadata, and cross-call state must be managed by the application itself.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="93-server-reverse-requests-vs-mrtr">9.3. Server Reverse Requests vs. MRTR<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#93-server-reverse-requests-vs-mrtr" class="hash-link" aria-label="Direct link to 9.3. Server Reverse Requests vs. MRTR" title="Direct link to 9.3. Server Reverse Requests vs. MRTR" translate="no">​</a></h3>
<p>In the old version, the server could proactively send requests (sampling/elicitation/roots), depending on bidirectional streams and stateful connections; MRTR changes it to "client retry + <code>inputResponses</code>," making the interaction conform to the unidirectional model of "the client only sends requests," a key supporting design for de-statification.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="94-mcp-vs-function-calling--lsp--a2a">9.4. MCP vs Function Calling / LSP / A2A<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#94-mcp-vs-function-calling--lsp--a2a" class="hash-link" aria-label="Direct link to 9.4. MCP vs Function Calling / LSP / A2A" title="Direct link to 9.4. MCP vs Function Calling / LSP / A2A" translate="no">​</a></h3>
<table><thead><tr><th>Comparison</th><th>Relationship</th></tr></thead><tbody><tr><td><strong>Function Calling</strong></td><td>MCP does not replace it; rather, it standardizes tool <strong>declaration and execution</strong>; the model side still uses each vendor's function calling</td></tr><tr><td><strong>LSP</strong></td><td>Architecturally homologous (Host/Client/Server, JSON-RPC); MCP is a migration of the LSP idea into the AI capability domain</td></tr><tr><td><strong>A2A</strong></td><td>MCP solves "AI ↔ tools," A2A solves "Agent ↔ Agent"; the two are complementary and composable</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="95-common-design-pitfalls">9.5. Common Design Pitfalls<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#95-common-design-pitfalls" class="hash-link" aria-label="Direct link to 9.5. Common Design Pitfalls" title="Direct link to 9.5. Common Design Pitfalls" translate="no">​</a></h3>
<ol>
<li class=""><strong>Name conflicts</strong>: when aggregating multiple Servers, tool names may collide, so prefix disambiguation is mandatory; <strong>do not</strong> rely on <code>serverInfo.name</code> (self-reported, unvalidated, and not guaranteed unique).</li>
<li class=""><strong>Version fragmentation</strong>: the 2025 series and the 2026 series coexist, so clients must be dual-era compatible.</li>
<li class=""><strong>SSE not recoverable</strong>: 2026-07-28 removes <code>Last-Event-ID</code>; a broken stream requires resending the entire request.</li>
<li class=""><strong>Stateless does not mean stateless data</strong>: cross-call state relies on the tool itself returning an explicit handle; the Server cannot depend on connection state.</li>
<li class=""><strong>stdio log pollution</strong>: the Server <strong>must never</strong> write non-MCP messages to <code>stdout</code>; logs go to <code>stderr</code>.</li>
<li class=""><strong>Missing timeouts</strong>: under a stateless protocol, the client must enforce timeouts, otherwise threads may hang forever.</li>
<li class=""><strong>Capability overreach</strong>: the Server <strong>MUST NOT</strong> rely on capabilities not declared by the client; otherwise it should return <code>-32021</code>.</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="10-summary-and-outlook">10. Summary and Outlook<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#10-summary-and-outlook" class="hash-link" aria-label="Direct link to 10. Summary and Outlook" title="Direct link to 10. Summary and Outlook" translate="no">​</a></h2>
<ol>
<li class="">MCP uses <strong>JSON-RPC 2.0 + three major primitives + standard transports</strong> to reduce the M×N integration cost of "AI application × external capability" to M+N, with a design paradigm inherited from LSP.</li>
<li class="">The version evolution reflects a clear mainline: <strong>from "stateful connection" to "stateless request"</strong>—<code>2025-03-26</code> switches the transport, <code>2025-06-18</code> strengthens structure and security, <code>2025-11-25</code> perfects authorization and the ecosystem, and <code>2026-07-28</code> thoroughly de-statifies and introduces MRTR/extension framework/deprecation policy.</li>
<li class="">The current version <code>2026-07-28</code> is a paradigm-level revision: self-describing requests, <code>server/discover</code>, MRTR, <code>subscriptions/listen</code>, the extension mechanism, and a formal deprecation policy.</li>
<li class="">The core of an MCP Client is <strong>Transport + protocol layer (id→Future routing, timeouts, cancellation) + tool adaptation</strong>; the core of a Server is <strong>method dispatch + capability declaration + error branching</strong>.</li>
<li class="">In terms of governance, MCP has evolved from a single-vendor open-source project into a neutral standard under the Linux Foundation's AAIF; going forward, watch the extension ecosystem (MCP Apps / Tasks), the registry (MCP Registry), and the implementation of authorization hardening.</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<blockquote>
<p>Official entry points: specification <a href="https://modelcontextprotocol.io/" target="_blank" rel="noopener noreferrer" class="">modelcontextprotocol.io</a> | official blog <a href="https://blog.modelcontextprotocol.io/" target="_blank" rel="noopener noreferrer" class="">blog.modelcontextprotocol.io</a> | official code <a href="https://github.com/modelcontextprotocol" target="_blank" rel="noopener noreferrer" class="">github.com/modelcontextprotocol</a> | official registry <a href="https://registry.modelcontextprotocol.io/" target="_blank" rel="noopener noreferrer" class="">registry.modelcontextprotocol.io</a></p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-official-specification-current-version-2026-07-28">1. Official Specification (Current Version 2026-07-28)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#1-official-specification-current-version-2026-07-28" class="hash-link" aria-label="Direct link to 1. Official Specification (Current Version 2026-07-28)" title="Direct link to 1. Official Specification (Current Version 2026-07-28)" translate="no">​</a></h2>
<p>[1]. <a href="https://modelcontextprotocol.io/specification/2026-07-28" target="_blank" rel="noopener noreferrer" class="">MCP Official Specification (2026-07-28)</a></p>
<p>[2]. <a href="https://modelcontextprotocol.io/llms.txt" target="_blank" rel="noopener noreferrer" class="">MCP Documentation Index llms.txt</a></p>
<p>[3]. <a href="https://modelcontextprotocol.io/specification/2026-07-28/architecture" target="_blank" rel="noopener noreferrer" class="">MCP Architecture Overview</a></p>
<p>[4]. <a href="https://modelcontextprotocol.io/specification/2026-07-28/basic" target="_blank" rel="noopener noreferrer" class="">MCP Basic Protocol and Message Model</a></p>
<p>[5]. <a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/lifecycle" target="_blank" rel="noopener noreferrer" class="">MCP Versioning and Compatibility</a></p>
<p>[6]. <a href="https://modelcontextprotocol.io/specification/2026-07-28/changelog" target="_blank" rel="noopener noreferrer" class="">MCP 2026-07-28 Key Changes</a></p>
<p>[7]. <a href="https://modelcontextprotocol.io/specification/2026-07-28/schema" target="_blank" rel="noopener noreferrer" class="">MCP Full Schema (schema.json / schema.ts)</a></p>
<p>[8]. <a href="https://modelcontextprotocol.io/specification/2026-07-28/deprecated" target="_blank" rel="noopener noreferrer" class="">MCP Deprecated Feature Registry</a></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="11-transports">1.1. Transports<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#11-transports" class="hash-link" aria-label="Direct link to 1.1. Transports" title="Direct link to 1.1. Transports" translate="no">​</a></h3>
<p>[9]. <a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/transports" target="_blank" rel="noopener noreferrer" class="">MCP Transports Overview</a></p>
<p>[10]. <a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/stdio" target="_blank" rel="noopener noreferrer" class="">MCP stdio Transport</a></p>
<p>[11]. <a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http" target="_blank" rel="noopener noreferrer" class="">MCP Streamable HTTP Transport</a></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="12-server-features">1.2. Server Features<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#12-server-features" class="hash-link" aria-label="Direct link to 1.2. Server Features" title="Direct link to 1.2. Server Features" translate="no">​</a></h3>
<p>[12]. <a href="https://modelcontextprotocol.io/specification/2026-07-28/server/tools" target="_blank" rel="noopener noreferrer" class="">MCP Tools</a></p>
<p>[13]. <a href="https://modelcontextprotocol.io/specification/2026-07-28/server/resources" target="_blank" rel="noopener noreferrer" class="">MCP Resources</a></p>
<p>[14]. <a href="https://modelcontextprotocol.io/specification/2026-07-28/server/prompts" target="_blank" rel="noopener noreferrer" class="">MCP Prompts</a></p>
<p>[15]. <a href="https://modelcontextprotocol.io/specification/2026-07-28/server/discover" target="_blank" rel="noopener noreferrer" class="">MCP Discovery (server/discover)</a></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="13-patterns--authorization">1.3. Patterns &amp; Authorization<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#13-patterns--authorization" class="hash-link" aria-label="Direct link to 1.3. Patterns &amp; Authorization" title="Direct link to 1.3. Patterns &amp; Authorization" translate="no">​</a></h3>
<p>[16]. <a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/patterns" target="_blank" rel="noopener noreferrer" class="">MCP Patterns Overview</a></p>
<p>[17]. <a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/patterns/mrtr" target="_blank" rel="noopener noreferrer" class="">MCP Multi Round-Trip Requests MRTR</a></p>
<p>[18]. <a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/patterns/subscriptions" target="_blank" rel="noopener noreferrer" class="">MCP Subscriptions and Notifications (subscriptions/listen)</a></p>
<p>[19]. <a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization" target="_blank" rel="noopener noreferrer" class="">MCP Authorization</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-historical-version-specifications">2. Historical Version Specifications<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#2-historical-version-specifications" class="hash-link" aria-label="Direct link to 2. Historical Version Specifications" title="Direct link to 2. Historical Version Specifications" translate="no">​</a></h2>
<p>[20]. <a href="https://modelcontextprotocol.io/specification/2025-11-25" target="_blank" rel="noopener noreferrer" class="">MCP 2025-11-25 Specification</a> | <a href="https://modelcontextprotocol.io/specification/2025-11-25/changelog" target="_blank" rel="noopener noreferrer" class="">Changelog</a></p>
<p>[21]. <a href="https://modelcontextprotocol.io/specification/2025-06-18" target="_blank" rel="noopener noreferrer" class="">MCP 2025-06-18 Specification</a> | <a href="https://modelcontextprotocol.io/specification/2025-06-18/changelog" target="_blank" rel="noopener noreferrer" class="">Changelog</a> | <a href="https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle" target="_blank" rel="noopener noreferrer" class="">Lifecycle (initialize handshake)</a></p>
<p>[22]. <a href="https://modelcontextprotocol.io/specification/2025-03-26" target="_blank" rel="noopener noreferrer" class="">MCP 2025-03-26 Specification</a> | <a href="https://modelcontextprotocol.io/specification/2025-03-26/changelog" target="_blank" rel="noopener noreferrer" class="">Changelog</a></p>
<p>[23]. <a href="https://modelcontextprotocol.io/specification/2024-11-05" target="_blank" rel="noopener noreferrer" class="">MCP 2024-11-05 Initial Specification</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-official-blog-blogmodelcontextprotocolio">3. Official Blog (blog.modelcontextprotocol.io)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#3-official-blog-blogmodelcontextprotocolio" class="hash-link" aria-label="Direct link to 3. Official Blog (blog.modelcontextprotocol.io)" title="Direct link to 3. Official Blog (blog.modelcontextprotocol.io)" translate="no">​</a></h2>
<p>[24]. <a href="https://blog.modelcontextprotocol.io/" target="_blank" rel="noopener noreferrer" class="">MCP Official Blog Home</a></p>
<p>[25]. <a href="https://blog.modelcontextprotocol.io/posts/2026-07-28/" target="_blank" rel="noopener noreferrer" class="">The 2026-07-28 Specification (Official Release)</a></p>
<p>[26]. <a href="https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/" target="_blank" rel="noopener noreferrer" class="">The 2026-07-28 MCP Specification Release Candidate</a></p>
<p>[27]. <a href="https://blog.modelcontextprotocol.io/posts/sdk-betas-2026-07-28/" target="_blank" rel="noopener noreferrer" class="">Beta SDKs for the 2026-07-28 MCP Spec RC</a></p>
<p>[28]. <a href="https://blog.modelcontextprotocol.io/posts/2026-01-26-mcp-apps/" target="_blank" rel="noopener noreferrer" class="">MCP Apps - Bringing UI Capabilities To MCP Clients</a></p>
<p>[29]. <a href="https://blog.modelcontextprotocol.io/posts/2025-11-25-first-mcp-anniversary/" target="_blank" rel="noopener noreferrer" class="">One Year of Model Context Protocol (2025-11-25 First Anniversary)</a></p>
<p>[30]. <a href="https://blog.modelcontextprotocol.io/posts/2025-09-08-mcp-registry-preview/" target="_blank" rel="noopener noreferrer" class="">Introducing the MCP Registry (2025-09-08 Preview)</a></p>
<p>[31]. <a href="https://blog.modelcontextprotocol.io/posts/2025-09-05-php-sdk/" target="_blank" rel="noopener noreferrer" class="">Announcing the official PHP SDK for MCP (2025-09-05)</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-official-repositories-and-sdks-githubcommodelcontextprotocol">4. Official Repositories and SDKs (github.com/modelcontextprotocol)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#4-official-repositories-and-sdks-githubcommodelcontextprotocol" class="hash-link" aria-label="Direct link to 4. Official Repositories and SDKs (github.com/modelcontextprotocol)" title="Direct link to 4. Official Repositories and SDKs (github.com/modelcontextprotocol)" translate="no">​</a></h2>
<p>[32]. <a href="https://github.com/modelcontextprotocol" target="_blank" rel="noopener noreferrer" class="">MCP GitHub Organization</a></p>
<p>[33]. <a href="https://github.com/modelcontextprotocol/modelcontextprotocol" target="_blank" rel="noopener noreferrer" class="">Specification and Documentation Repository modelcontextprotocol</a></p>
<p>[34]. <a href="https://github.com/modelcontextprotocol/docs" target="_blank" rel="noopener noreferrer" class="">Documentation Repository docs</a></p>
<p>[35]. <a href="https://github.com/modelcontextprotocol/servers" target="_blank" rel="noopener noreferrer" class="">Reference Server Repository servers</a></p>
<p>[36]. <a href="https://github.com/modelcontextprotocol/inspector" target="_blank" rel="noopener noreferrer" class="">Debugging Tool MCP Inspector</a></p>
<p>[37]. <a href="https://github.com/modelcontextprotocol/registry" target="_blank" rel="noopener noreferrer" class="">Registry registry</a> | <a href="https://registry.modelcontextprotocol.io/" target="_blank" rel="noopener noreferrer" class="">Official Registry Site</a></p>
<p>[38]. <a href="https://github.com/modelcontextprotocol/conformance" target="_blank" rel="noopener noreferrer" class="">Conformance Testing conformance</a></p>
<p>[39]. <a href="https://github.com/modelcontextprotocol/python-sdk" target="_blank" rel="noopener noreferrer" class="">Official SDKs: Python</a> | <a href="https://github.com/modelcontextprotocol/typescript-sdk" target="_blank" rel="noopener noreferrer" class="">TypeScript</a> | <a href="https://github.com/modelcontextprotocol/java-sdk" target="_blank" rel="noopener noreferrer" class="">Java</a> | <a href="https://github.com/modelcontextprotocol/csharp-sdk" target="_blank" rel="noopener noreferrer" class="">C#</a> | <a href="https://github.com/modelcontextprotocol/go-sdk" target="_blank" rel="noopener noreferrer" class="">Go</a> | <a href="https://github.com/modelcontextprotocol/kotlin-sdk" target="_blank" rel="noopener noreferrer" class="">Kotlin</a> | <a href="https://github.com/modelcontextprotocol/php-sdk" target="_blank" rel="noopener noreferrer" class="">PHP</a> | <a href="https://github.com/modelcontextprotocol/ruby-sdk" target="_blank" rel="noopener noreferrer" class="">Ruby</a> | <a href="https://github.com/modelcontextprotocol/rust-sdk" target="_blank" rel="noopener noreferrer" class="">Rust</a> | <a href="https://github.com/modelcontextprotocol/swift-sdk" target="_blank" rel="noopener noreferrer" class="">Swift</a></p>
<p>[40]. <a href="https://github.com/modelcontextprotocol/ext-apps" target="_blank" rel="noopener noreferrer" class="">Official Extensions: MCP Apps (ext-apps)</a> | <a href="https://github.com/modelcontextprotocol/ext-tasks" target="_blank" rel="noopener noreferrer" class="">Tasks (ext-tasks)</a> | <a href="https://github.com/modelcontextprotocol/ext-auth" target="_blank" rel="noopener noreferrer" class="">Auth (ext-auth)</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-anthropic-announcements">5. Anthropic Announcements<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#5-anthropic-announcements" class="hash-link" aria-label="Direct link to 5. Anthropic Announcements" title="Direct link to 5. Anthropic Announcements" translate="no">​</a></h2>
<p>[41]. <a href="https://www.anthropic.com/news/model-context-protocol" target="_blank" rel="noopener noreferrer" class="">Introducing the Model Context Protocol (2024-11-25 Open-Source Release)</a></p>
<p>[42]. <a href="https://www.anthropic.com/news/donating-the-model-context-protocol-and-establishing-of-the-agentic-ai-foundation" target="_blank" rel="noopener noreferrer" class="">Donating the Model Context Protocol and establishing the Agentic AI Foundation (2025-12-09)</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-related-standards-and-specifications">6. Related Standards and Specifications<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#6-related-standards-and-specifications" class="hash-link" aria-label="Direct link to 6. Related Standards and Specifications" title="Direct link to 6. Related Standards and Specifications" translate="no">​</a></h2>
<p>[43]. <a href="https://www.jsonrpc.org/specification" target="_blank" rel="noopener noreferrer" class="">JSON-RPC 2.0 Specification</a></p>
<p>[44]. <a href="https://microsoft.github.io/language-server-protocol/" target="_blank" rel="noopener noreferrer" class="">Language Server Protocol (LSP, the Inspiration for MCP)</a></p>
<p>[45]. <a href="https://json-schema.org/draft/2020-12/schema" target="_blank" rel="noopener noreferrer" class="">JSON Schema 2020-12</a></p>
<p>[46]. <a href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1" target="_blank" rel="noopener noreferrer" class="">OAuth 2.1 (IETF draft)</a></p>
<p>[47]. <a href="https://datatracker.ietf.org/doc/html/rfc8707" target="_blank" rel="noopener noreferrer" class="">RFC 8707 Resource Indicators for OAuth 2.0</a></p>
<p>[48]. <a href="https://datatracker.ietf.org/doc/html/rfc9207" target="_blank" rel="noopener noreferrer" class="">RFC 9207 OAuth 2.0 Authorization Server Issuer Identification</a></p>
<p>[49]. <a href="https://datatracker.ietf.org/doc/html/rfc7591" target="_blank" rel="noopener noreferrer" class="">RFC 7591 OAuth 2.0 Dynamic Client Registration (Deprecated by MCP)</a></p>
<p>[50]. <a href="https://openid.net/specs/openid-connect-discovery-1_0.html" target="_blank" rel="noopener noreferrer" class="">OpenID Connect Discovery 1.0</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-further-reading-third-party">7. Further Reading (Third-Party)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#7-further-reading-third-party" class="hash-link" aria-label="Direct link to 7. Further Reading (Third-Party)" title="Direct link to 7. Further Reading (Third-Party)" translate="no">​</a></h2>
<p>[51]. <a href="https://hidekazu-konishi.com/entry/mcp_specification_version_timeline.html" target="_blank" rel="noopener noreferrer" class="">Model Context Protocol Specification Version Timeline</a></p>
<p>[52]. <a href="https://docs.langchain4j.dev/tutorials/mcp/" target="_blank" rel="noopener noreferrer" class="">LangChain4j MCP Official Tutorial</a></p>
<p>[53]. <a href="https://docs.spring.io/spring-ai/reference/api/mcp/mcp-helpers.html" target="_blank" rel="noopener noreferrer" class="">Spring AI MCP Utilities</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-java-build-and-agent-integration-references">8. Java Build and Agent Integration References<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81MCP%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3-%E5%8E%86%E5%8F%B2%E6%BC%94%E8%BF%9B%E4%B8%8E%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86#8-java-build-and-agent-integration-references" class="hash-link" aria-label="Direct link to 8. Java Build and Agent Integration References" title="Direct link to 8. Java Build and Agent Integration References" translate="no">​</a></h2>
<p>[54]. <a href="https://java.sdk.modelcontextprotocol.io/latest/" target="_blank" rel="noopener noreferrer" class="">MCP Java SDK Official Documentation</a></p>
<p>[55]. <a href="https://java.sdk.modelcontextprotocol.io/latest/quickstart/" target="_blank" rel="noopener noreferrer" class="">MCP Java SDK Quickstart (Dependencies / BOM)</a></p>
<p>[56]. <a href="https://java.sdk.modelcontextprotocol.io/latest/client/" target="_blank" rel="noopener noreferrer" class="">MCP Java SDK Client</a></p>
<p>[57]. <a href="https://java.sdk.modelcontextprotocol.io/latest/server/" target="_blank" rel="noopener noreferrer" class="">MCP Java SDK Server</a></p>
<p>[58]. <a href="https://java.agentscope.io/v1/en/docs/task/mcp" target="_blank" rel="noopener noreferrer" class="">AgentScope Java MCP Documentation</a></p>
<p>[59]. <a href="https://github.com/agentscope-ai/agentscope-java" target="_blank" rel="noopener noreferrer" class="">AgentScope Java (GitHub)</a></p>
<p>[60]. <a href="https://doc.agentscope.io/tutorial/task_mcp.html" target="_blank" rel="noopener noreferrer" class="">AgentScope Python MCP Tutorial</a></p>
<p>[61]. <a href="https://docs.spring.io/spring-ai/reference/api/mcp/mcp-overview.html" target="_blank" rel="noopener noreferrer" class="">Spring AI MCP Overview</a></p>
<p>[62]. <a href="https://docs.spring.io/spring-ai/reference/api/mcp/mcp-client-boot-starter-docs.html" target="_blank" rel="noopener noreferrer" class="">Spring AI MCP Client Boot Starter</a></p>
<br>
<p>Compiler: Changlu  Created: 2026.10.5  Updated: 2026.10.5</p>]]></content:encoded>
            <category>AgentForge</category>
            <category>General ReAct Agent Layer</category>
            <category>Function Calling</category>
            <category>MCP</category>
        </item>
        <item>
            <title><![CDATA[Adapting HTTP Protocol to FunctionCall Protocol 01, Core Design Principles of the AgentForge HTTP Tool Module]]></title>
            <link>https://agentforge-tech.github.io/AgentForge/en/blog/02、通用ReActagent层/functioncall核心扩展业务tool/http/适配HTTP协议转换FunctionCall协议01、AgentForge HTTP工具模块核心设计原理</link>
            <guid>https://agentforge-tech.github.io/AgentForge/en/blog/02、通用ReActagent层/functioncall核心扩展业务tool/http/适配HTTP协议转换FunctionCall协议01、AgentForge HTTP工具模块核心设计原理</guid>
            <pubDate>Thu, 08 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Updated: 2026-10-05]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>Updated: 2026-10-05<br>
<!-- -->Applicable module: <code>agentforge-agent-core</code> (<code>cloud.changlu.agentforge.agent.tool.http</code>)<br>
<!-- -->Dependent contracts: <code>agentforge-model-api</code> / <code>agentforge-model-core</code><br>
<!-- -->Maintainer: changlu</p>
</blockquote>
<!-- -->
<p>One-sentence conclusion: <strong>The HTTP tool module declaratively maps "a set of HTTP endpoints" into AgentForge's <code>ToolSpecification</code> + <code>ToolExecutor</code>, letting the model call any REST endpoint just like calling a local method—without introducing any HTTP / JSON third-party library into the zero-dependency core.</strong></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-background-and-problem-introduction">1. Background and Problem Introduction<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#1-background-and-problem-introduction" class="hash-link" aria-label="Direct link to 1. Background and Problem Introduction" title="Direct link to 1. Background and Problem Introduction" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="11-scenario-driven-where-we-got-stuck-while-integrating-a-dingtalk-bot">1.1 Scenario-driven: where we got stuck while integrating a DingTalk bot<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#11-scenario-driven-where-we-got-stuck-while-integrating-a-dingtalk-bot" class="hash-link" aria-label="Direct link to 1.1 Scenario-driven: where we got stuck while integrating a DingTalk bot" title="Direct link to 1.1 Scenario-driven: where we got stuck while integrating a DingTalk bot" translate="no">​</a></h2>
<p>While building external capability integration for AgentForge, we encountered a very realistic scenario:</p>
<blockquote>
<p>The team wants the Agent to send DingTalk messages, query CRM customers, and pull ticket lists. These capabilities <strong>have neither an MCP Server, nor are worth writing a Java <code>@Tool</code> method for each endpoint</strong>—they are just ordinary REST APIs.</p>
</blockquote>
<p>This brought three concrete pain points:</p>
<ol>
<li class=""><strong>Many endpoints</strong>: A single SaaS has dozens of endpoints; writing <code>@Tool</code> methods one by one has an extremely high maintenance cost;</li>
<li class=""><strong>The model has no way to start</strong>: Handing "a generic HTTP tool" to the model makes it invent URLs and add parameters arbitrarily—neither controllable nor safe;</li>
<li class=""><strong>Zero-dependency core</strong>: AgentForge's core is Java 8 + zero third-party libraries, so OkHttp / Retrofit / Jackson cannot be introduced for HTTP access.</li>
</ol>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="12-problem-led-how-to-let-the-model-call-http-by-speaking-natural-language">1.2 Problem-led: how to let the model call HTTP by "speaking natural language"?<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#12-problem-led-how-to-let-the-model-call-http-by-speaking-natural-language" class="hash-link" aria-label="Direct link to 1.2 Problem-led: how to let the model call HTTP by &quot;speaking natural language&quot;?" title="Direct link to 1.2 Problem-led: how to let the model call HTTP by &quot;speaking natural language&quot;?" translate="no">​</a></h2>
<blockquote>
<p><strong>Problem</strong>: How can the model turn a natural-language sentence into an HTTP call with <strong>correct parameters and a controllable address</strong>?</p>
</blockquote>
<p>The answer is not "let the model assemble HTTP itself", but rather:</p>
<ul>
<li class="">The developer <strong>pre-declares</strong> each endpoint (address, method, parameters) → generating a <code>ToolSpecification</code>;</li>
<li class="">The model only chooses among <strong>declared tools</strong> and fills in only <strong>declared parameters</strong>;</li>
<li class="">At runtime, the executor "fills" the parameters into the URL / Header / Body and issues the request.</li>
</ul>
<p>This is exactly what this module does.</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="13-design-goals-and-constraints">1.3 Design goals and constraints<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#13-design-goals-and-constraints" class="hash-link" aria-label="Direct link to 1.3 Design goals and constraints" title="Direct link to 1.3 Design goals and constraints" translate="no">​</a></h2>
<table><thead><tr><th>Goal</th><th>Description</th></tr></thead><tbody><tr><td>Declarative</td><td>Describe a group of endpoints with an <code>HttpPlugin</code>; write no glue code</td></tr><tr><td>Zero dependencies</td><td>Reuse the built-in <code>Json</code> and <code>HttpTransport</code> (<code>JdkHttpTransport</code>)</td></tr><tr><td>Controllable</td><td>The model can only call declared tools; URLs are fixed by the plugin</td></tr><tr><td>Unified</td><td>The output is <code>Map&lt;ToolSpecification, ToolExecutor&gt;</code>, the same tool table as local / mcp</td></tr><tr><td>Extensible</td><td><code>IHttpPlugin</code> SPI + curl interconversion, lowering integration cost</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-core-concepts">2. Core Concepts<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#2-core-concepts" class="hash-link" aria-label="Direct link to 2. Core Concepts" title="Direct link to 2. Core Concepts" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-three-core-abstractions">2.1 Three core abstractions<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#21-three-core-abstractions" class="hash-link" aria-label="Direct link to 2.1 Three core abstractions" title="Direct link to 2.1 Three core abstractions" translate="no">​</a></h2>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">HttpPlugin                    # 一个服务：baseUrl + 公共请求头 + 一组方法</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  └── HttpPluginMethod        # 一个工具：name / description / httpMethod / uri / parameters</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        └── HttpToolParameter # 一个参数：参数名 + 映射名 + 使用位置 + 数据类型 + 默认值 + 是否必填</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-the-four-dimensional-parameter-model-key">2.2 The four-dimensional parameter model (key)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#22-the-four-dimensional-parameter-model-key" class="hash-link" aria-label="Direct link to 2.2 The four-dimensional parameter model (key)" title="Direct link to 2.2 The four-dimensional parameter model (key)" translate="no">​</a></h2>
<p>A parameter is described by four dimensions, and this is precisely what makes the mapping from "declaration" to "actual request" possible:</p>
<table><thead><tr><th>Dimension</th><th>Enum</th><th>Purpose</th></tr></thead><tbody><tr><td>Use location</td><td><code>ParameterUseType</code>: QUERY / BODY / PATH / HEADER</td><td>Determines whether the parameter ultimately lands in the URL, request body, or request header</td></tr><tr><td>Data type</td><td><code>ParameterType</code>: STRING / INTEGER / NUMBER / BOOLEAN / ARRAY / OBJECT</td><td>Determines the JSON Schema type and runtime type conversion</td></tr><tr><td>Mapped name</td><td><code>mappedName</code></td><td>Model parameter name ↔ actual endpoint parameter name (internal/external decoupling, renamable)</td></tr><tr><td>Constraint</td><td><code>defaultValue</code> / <code>required</code></td><td>Default filling and required validation</td></tr></tbody></table>
<blockquote>
<p><strong>Key point</strong>: What the model sees is <code>methodParamName</code> (the tool parameter name), while what is actually sent to the endpoint is <code>mappedName</code>—this lets "the name shown to the model" and "the name the endpoint wants" differ.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="23-relationship-with-the-agent-tool-abstraction">2.3 Relationship with the Agent tool abstraction<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#23-relationship-with-the-agent-tool-abstraction" class="hash-link" aria-label="Direct link to 2.3 Relationship with the Agent tool abstraction" title="Direct link to 2.3 Relationship with the Agent tool abstraction" translate="no">​</a></h2>
<table><thead><tr><th>HTTP module</th><th>AgentForge contract</th></tr></thead><tbody><tr><td><code>HttpPluginMethod</code> + parameters</td><td><code>ToolSpecification</code> (<code>parameters</code> → JSON Schema)</td></tr><tr><td><code>HttpToolExecutor</code></td><td><code>ToolExecutor</code> (<code>execute</code> / <code>executeWithResult</code>)</td></tr><tr><td><code>HttpToolFactory.buildHttpTools</code></td><td>Produces <code>Map&lt;ToolSpecification, ToolExecutor&gt;</code>, handed to <code>ToolService</code></td></tr></tbody></table>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="24-why-curl-support-is-also-needed">2.4 Why curl support is also needed<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#24-why-curl-support-is-also-needed" class="hash-link" aria-label="Direct link to 2.4 Why curl support is also needed" title="Direct link to 2.4 Why curl support is also needed" translate="no">​</a></h2>
<p>The most common "endpoint sample" in a developer's hands is a curl command. Supporting <code>curl → HttpPlugin</code> lets "copy-paste a curl command" directly become an Agent-injectable tool; the reverse <code>HttpPlugin → curl</code> is convenient for debugging and documentation.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-implementation-approach">3. Implementation Approach<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#3-implementation-approach" class="hash-link" aria-label="Direct link to 3. Implementation Approach" title="Direct link to 3. Implementation Approach" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-comparison-of-three-approaches">3.1 Comparison of three approaches<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#31-comparison-of-three-approaches" class="hash-link" aria-label="Direct link to 3.1 Comparison of three approaches" title="Direct link to 3.1 Comparison of three approaches" translate="no">​</a></h2>
<p><strong>Approach 1: Write a <code>@Tool</code> method for every endpoint</strong></p>
<ul>
<li class="">Pros: Type-safe, can reuse the local pattern.</li>
<li class="">Drawbacks: Code bloat when there are many endpoints; changing one field requires a code change; high maintenance cost.</li>
</ul>
<p><strong>Approach 2: A single generic HTTP tool (the model passes url / method / body)</strong></p>
<ul>
<li class="">Pros: Simplest implementation.</li>
<li class="">Drawbacks: <strong>The model can construct arbitrary URLs / parameters</strong>, uncontrollable, unsafe, and prone to inventing endpoints; hard to do authentication and allowlisting.</li>
</ul>
<p><strong>Approach 3: Declarative <code>HttpPlugin</code> mapping (adopted by this module)</strong></p>
<ul>
<li class="">Pros: Fixed addresses, controlled parameters, zero dependencies, batch description, and can be unified into <code>ToolService</code> with local / mcp.</li>
<li class="">Drawbacks: Requires defining the plugin structure; complex authentication (OAuth refresh, etc.) still needs to be supplemented at the host layer.</li>
</ul>
<blockquote>
<p><strong>Conclusion</strong>: Adopt <strong>Approach 3</strong>—trade declarations for controllability, and mapping for zero dependencies.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-core-flow-list--spec--executor">3.2 Core flow: List → Spec + Executor<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#32-core-flow-list--spec--executor" class="hash-link" aria-label="Direct link to 3.2 Core flow: List → Spec + Executor" title="Direct link to 3.2 Core flow: List → Spec + Executor" translate="no">​</a></h2>
<p><code>HttpToolFactory.buildHttpTools(httpPlugin)</code> produces a pair for each <code>HttpPluginMethod</code>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">HttpPluginMethod</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   ├─ methodName / description ───────────► ToolSpecification.name / description</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   ├─ parameters[].toJsonSchema() ────────► ToolSpecification.parameters（JSON Schema）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   └─ (baseUrl+uri, httpMethod, headers, 参数配置) ─► new HttpToolExecutor(...)</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: Unlike MCP's <code>serverAlias__tool</code>, HTTP tools are <strong>not prefixed</strong>—the tool name directly uses <code>methodName</code>, and uniqueness is naturally under the developer's control.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-parameter-processing-pipeline-core">3.3 Parameter processing pipeline (core)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#33-parameter-processing-pipeline-core" class="hash-link" aria-label="Direct link to 3.3 Parameter processing pipeline (core)" title="Direct link to 3.3 Parameter processing pipeline (core)" translate="no">​</a></h2>
<p>Inside <code>HttpToolExecutor.execute</code>, processing follows a fixed order:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">arguments(JSON) ──► argumentsAsMap</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">processArguments      ① 取实参 / 套默认值 / 校验必填 / 按 dataType 转换</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ├─ buildUrlWithPathParams   ② PATH 参数替换 {mappedName}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ├─ addHeaderParameters      ③ HEADER 参数写入请求头</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ├─ shouldUseJsonBody ?</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │     ├─ true  → BODY 参数 → JSON Body（Content-Type: application/json）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │     └─ false → appendQueryParameters（QUERY 参数拼到 URL）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">HttpRequest(url, method, headers, body) ──► HttpTransport.execute</span><br></div></code></pre></div></div>
<ul>
<li class=""><strong>Missing required parameter</strong>: throws <code>IllegalArgumentException("缺少必填参数 'x'")</code>;</li>
<li class=""><strong>Type conversion</strong>: <code>convertValueToCorrectType</code> supports INTEGER / NUMBER / BOOLEAN / ARRAY / OBJECT / STRING, and performs tolerant parsing of JSON strings.</li>
</ul>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="34-intelligent-judgment-of-the-request-body">3.4 "Intelligent judgment" of the request body<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#34-intelligent-judgment-of-the-request-body" class="hash-link" aria-label="Direct link to 3.4 &quot;Intelligent judgment&quot; of the request body" title="Direct link to 3.4 &quot;Intelligent judgment&quot; of the request body" translate="no">​</a></h2>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">GET / DELETE   → 强制用 Query（即使配了 BODY 也不发 Body）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">POST / PUT / PATCH → 只有当存在 BODY 参数（已传或有默认值）时才发 JSON Body</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Note</strong>: GET does not send a request body even if BODY-type parameters are configured (guaranteed by the <code>shouldNotSendJsonBodyForGetEvenWithBodyConfig</code> case).</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="35-retry-and-fault-tolerance">3.5 Retry and fault tolerance<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#35-retry-and-fault-tolerance" class="hash-link" aria-label="Direct link to 3.5 Retry and fault tolerance" title="Direct link to 3.5 Retry and fault tolerance" translate="no">​</a></h2>
<ul>
<li class="">Retry <strong>once</strong> only on <code>connection reset</code> for <strong>GET / DELETE</strong> (safe under idempotent semantics);</li>
<li class="">Other exceptions are caught in <code>execute</code> and degraded to the text <code>"HTTP请求失败: {message}"</code>.</li>
</ul>
<blockquote>
<p><strong>Drawbacks</strong>: The current <code>execute</code> degrades errors to <strong>text</strong> while <code>isError</code> remains <code>false</code>; when structured errors are needed, wrap them at the upper layer with <code>ToolExecutionResult</code> (for comparison: the MCP executor already performs lossless <code>isError</code> mapping).</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="36-curl-parsing-and-interconversion">3.6 curl parsing and interconversion<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#36-curl-parsing-and-interconversion" class="hash-link" aria-label="Direct link to 3.6 curl parsing and interconversion" title="Direct link to 3.6 curl parsing and interconversion" translate="no">​</a></h2>
<table><thead><tr><th>Direction</th><th>Entry point</th><th>Description</th></tr></thead><tbody><tr><td>curl → plugin</td><td><code>CurlParser.parse</code> + <code>CurlToHttpPluginConverter.convert</code></td><td>Splits out url / method / headers / query / body and generates parameters</td></tr><tr><td>plugin → curl</td><td><code>HttpPluginToCurlConverter.convert(plugin[, methodName])</code></td><td>Generates a reproducible command in reverse; for multi-method plugins a method name must be specified</td></tr></tbody></table>
<blockquote>
<p><strong>Note</strong>: When a plugin contains multiple methods, <code>convert(plugin)</code> is rejected and <code>methodName</code> must be specified explicitly.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="37-capability-matrix-request-methods-and-parameter-formats">3.7 Capability matrix: request methods and parameter formats<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#37-capability-matrix-request-methods-and-parameter-formats" class="hash-link" aria-label="Direct link to 3.7 Capability matrix: request methods and parameter formats" title="Direct link to 3.7 Capability matrix: request methods and parameter formats" translate="no">​</a></h2>
<p><strong>① Supported HTTP methods (<code>HttpPluginEnums.HttpMethod</code>)</strong></p>
<table><thead><tr><th>Method</th><th>Code</th><th>Request body</th><th>Idempotent retry</th><th>Description</th></tr></thead><tbody><tr><td>GET</td><td>1</td><td>No (<strong>forced Query</strong>)</td><td>✅ Retry once on connection reset</td><td>Query</td></tr><tr><td>POST</td><td>2</td><td>JSON when there is a BODY parameter</td><td>❌</td><td>Create</td></tr><tr><td>PUT</td><td>3</td><td>JSON when there is a BODY parameter</td><td>❌</td><td>Full update</td></tr><tr><td>DELETE</td><td>4</td><td>No (forced Query)</td><td>✅ Retry once on connection reset</td><td>Delete (idempotent)</td></tr><tr><td>PATCH</td><td>5</td><td>JSON when there is a BODY parameter</td><td>❌</td><td>Partial update</td></tr></tbody></table>
<blockquote>
<p><strong>Key point</strong>: Parameters configured as BODY for GET / DELETE <strong>are not</strong> put into the request body and are ignored (see <code>shouldNotSendJsonBodyForGetEvenWithBodyConfig</code>).</p>
</blockquote>
<br>
<p><strong>② Parameter use locations (<code>ParameterUseType</code>)</strong></p>
<table><thead><tr><th>Location</th><th>Code</th><th>Landing point</th><th>Generation rule</th></tr></thead><tbody><tr><td>QUERY</td><td>1</td><td>URL query string</td><td><code>encode(mappedName)=encode(value)</code></td></tr><tr><td>BODY</td><td>2</td><td>JSON request body</td><td><code>{mappedName: value}</code> → <code>Json.stringify</code></td></tr><tr><td>PATH</td><td>3</td><td>URL path placeholder</td><td>Replace <code>{mappedName}</code></td></tr><tr><td>HEADER</td><td>4</td><td>Request header</td><td><code>mappedName: value</code></td></tr></tbody></table>
<br>
<p><strong>③ Parameter data types (<code>ParameterType</code>)</strong></p>
<table><thead><tr><th>Type</th><th>Code</th><th>JSON Schema</th><th>Runtime conversion</th></tr></thead><tbody><tr><td>STRING</td><td>1</td><td><code>string</code></td><td><code>value.toString()</code></td></tr><tr><td>INTEGER</td><td>2</td><td><code>integer</code></td><td><code>Integer.parseInt</code> / <code>Number.intValue()</code></td></tr><tr><td>NUMBER</td><td>3</td><td><code>number</code></td><td><code>Double.parseDouble</code> / <code>Number.doubleValue()</code></td></tr><tr><td>BOOLEAN</td><td>4</td><td><code>boolean</code></td><td><code>"true"/"1"/"yes"</code> → true, otherwise false</td></tr><tr><td>ARRAY</td><td>5</td><td><code>array</code> (items: string)</td><td>List used directly; JSON string parsed; otherwise wrapped as a single-element array</td></tr><tr><td>OBJECT</td><td>6</td><td><code>object</code></td><td>Map used directly; JSON string parsed; otherwise an empty Map</td></tr></tbody></table>
<br>
<p><strong>④ Required and default values (<code>RequiredStatus</code> + <code>defaultValue</code>)</strong></p>
<table><thead><tr><th>Item</th><th>Code</th><th>Behavior</th></tr></thead><tbody><tr><td>REQUIRED</td><td>1</td><td>Both the actual argument and the default value are missing → throws <code>IllegalArgumentException("缺少必填参数 'x'")</code></td></tr><tr><td>NOT_REQUIRED</td><td>0</td><td>If missing, it does not participate in this request</td></tr></tbody></table>
<blockquote>
<p><strong>Key point</strong>: The value priority is <strong>actual argument &gt; <code>defaultValue</code> &gt; required validation</strong>; <code>methodParamName</code> is the model-side name, and <code>mappedName</code> is the name sent to the endpoint.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-practical-code">4. Practical Code<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#4-practical-code" class="hash-link" aria-label="Direct link to 4. Practical Code" title="Direct link to 4. Practical Code" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="41-minimal-integration">4.1 Minimal integration<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#41-minimal-integration" class="hash-link" aria-label="Direct link to 4.1 Minimal integration" title="Direct link to 4.1 Minimal integration" translate="no">​</a></h2>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">HttpPlugin</span><span class="token plain"> plugin </span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">HttpPlugin</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">baseUrl</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"https://api.example.com"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">staticHeaders</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Collections</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">singletonMap</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Authorization"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Bearer "</span><span class="token plain"> </span><span class="token operator">+</span><span class="token plain"> token</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">pluginMethods</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token class-name">Collections</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">singletonList</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                </span><span class="token class-name">HttpPluginMethod</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">methodName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"getWeather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">methodDescription</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"查询城市天气"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">httpMethodType</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">HttpPluginEnums</span><span class="token class-name punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token class-name">HttpMethod</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">GET</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getValue</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">uri</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"/v1/weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">parameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                                </span><span class="token class-name">Collections</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">singletonList</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                                        </span><span class="token class-name">HttpToolParameter</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                                                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">methodParamName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"city"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                                                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">methodParamDescription</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"城市名"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                                                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">mappedName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"q"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                                                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">useTypeValue</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                                                        </span><span class="token class-name">HttpPluginEnums</span><span class="token class-name punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token class-name">ParameterUseType</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                                                                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">QUERY</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                                                                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getValue</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                                                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">dataTypeValue</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                                                        </span><span class="token class-name">HttpPluginEnums</span><span class="token class-name punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token class-name">ParameterType</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                                                                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">STRING</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                                                                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getValue</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                                                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">required</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                                                        </span><span class="token class-name">HttpPluginEnums</span><span class="token class-name punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token class-name">RequiredStatus</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                                                                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">REQUIRED</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                                                                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getCode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                                                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ToolService</span><span class="token plain"> toolService </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">ToolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">HttpToolFactory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">buildHttpTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">plugin</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ReActAgent</span><span class="token plain"> agent </span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">ReActAgent</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">chatModel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">chatModel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p><strong>Run output (simulated terminal)</strong></p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># 1) 注册后模型可见的工具（tools[] 摘要）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">getWeather</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> 查询城市天气</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  parameters: </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> city: string </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">required</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain">   </span><span class="token comment" style="color:rgb(98, 114, 164)"># 模型参数名 city → 实际接口参数名 ?q</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># 2) 执行 getWeather(city=杭州) 的 HTTP 往返（java.util.logging，INFO）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">==</span><span class="token operator">=</span><span class="token plain"> HTTP 请求详情 </span><span class="token operator">==</span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">URL: https://api.example.com/v1/weather?q</span><span class="token operator">=</span><span class="token plain">%E6%9D%AD%E5%B7%9E</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">方法: GET</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">请求头: </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">Authorization</span><span class="token operator">=</span><span class="token plain">Bearer sk-***</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">QUERY 参数: </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">q</span><span class="token operator">=</span><span class="token plain">杭州</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">==</span><span class="token operator">=</span><span class="token plain"> HTTP 响应详情 </span><span class="token operator">==</span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">状态码: </span><span class="token number">200</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">耗时: </span><span class="token number">143</span><span class="token plain"> 毫秒</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">响应体: </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"city"</span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token string" style="color:rgb(255, 121, 198)">"杭州"</span><span class="token plain">,</span><span class="token string" style="color:rgb(255, 121, 198)">"temp"</span><span class="token plain">:26,</span><span class="token string" style="color:rgb(255, 121, 198)">"text"</span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token string" style="color:rgb(255, 121, 198)">"晴"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">=</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="42-extending-via-ihttpplugin">4.2 Extending via <code>IHttpPlugin</code><a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#42-extending-via-ihttpplugin" class="hash-link" aria-label="Direct link to 42-extending-via-ihttpplugin" title="Direct link to 42-extending-via-ihttpplugin" translate="no">​</a></h2>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">DingTalkPlugin</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">implements</span><span class="token plain"> </span><span class="token class-name">IHttpPlugin</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token class-name">Properties</span><span class="token plain"> props</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Override</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">init</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Properties</span><span class="token plain"> props</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">this</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">props </span><span class="token operator">=</span><span class="token plain"> props</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Override</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">getPluginName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"dingtalk"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Override</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">doCheckProps</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">props</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getProperty</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"token"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">IllegalArgumentException</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"missing token"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Override</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token class-name">HttpPlugin</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">getHttpPlugin</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">/* 依据 props 构建 */</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> plugin</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">DingTalkPlugin</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">buildHttpTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="43-one-click-curl-conversion">4.3 One-click curl conversion<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#43-one-click-curl-conversion" class="hash-link" aria-label="Direct link to 4.3 One-click curl conversion" title="Direct link to 4.3 One-click curl conversion" translate="no">​</a></h2>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">CurlParseResult</span><span class="token plain"> curl </span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">CurlParser</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">parse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token string" style="color:rgb(255, 121, 198)">"curl -X POST 'https://api.example.com/v1/tickets' "</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                </span><span class="token operator">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"-H 'Content-Type: application/json' "</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                </span><span class="token operator">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"-d '{\"title\":\"bug\"}'"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">HttpPlugin</span><span class="token plain"> plugin </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">CurlToHttpPluginConverter</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">convert</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">curl</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">HttpToolFactory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">buildHttpTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">plugin</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p><strong>Run output (simulated terminal)</strong></p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># CurlParser.parse(...) 解析出的关键字段</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">method  </span><span class="token operator">=</span><span class="token plain"> POST</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">url     </span><span class="token operator">=</span><span class="token plain"> https://api.example.com/v1/tickets</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">headers </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">Content-Type</span><span class="token operator">=</span><span class="token plain">application/json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data    </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"title"</span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token string" style="color:rgb(255, 121, 198)">"bug"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># CurlToHttpPluginConverter.convert(...) 生成的 HttpPlugin</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">baseUrl </span><span class="token operator">=</span><span class="token plain"> https://api.example.com</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">uri     </span><span class="token operator">=</span><span class="token plain"> /v1/tickets</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">method  </span><span class="token operator">=</span><span class="token plain"> POST</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">params  </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> title: string </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">BODY:title, required</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># buildHttpTools(...) → 模型可见工具（方法名由 curl 自动生成 executePOST）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">executePOST</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> （由 </span><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> 生成）  body: </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"title"</span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"..."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="44-how-a-single-model-invocation-is-closed-out">4.4 How a single model invocation is closed out<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#44-how-a-single-model-invocation-is-closed-out" class="hash-link" aria-label="Direct link to 4.4 How a single model invocation is closed out" title="Direct link to 4.4 How a single model invocation is closed out" translate="no">​</a></h2>
<blockquote>
<p><strong>Corresponding unit test</strong>: <code>HttpToolLoopTest#shouldCloseTheWeatherLoopThroughToolService</code>—injects a <code>RecordingHttpTransport</code> and runs the full offline path "declare → register → hit → assemble → send request → feed back".</p>
</blockquote>
<p>Using <code>getWeather</code> from 4.1 as an example, breaking it down step by step:</p>
<p><strong>① Declaration and registration (build time)</strong></p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ToolSpecification</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">ToolExecutor</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> tools </span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">HttpToolFactory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">buildHttpTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token function" style="color:rgb(80, 250, 123)">weatherPlugin</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> transport</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">// transport 可注入（测试用假实现）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ToolService</span><span class="token plain"> toolService </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">ToolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">                                           </span><span class="token comment" style="color:rgb(98, 114, 164)">// toolExecutors["getWeather"] = executor</span><br></div></code></pre></div></div>
<ul>
<li class=""><code>ToolSpecification.name = "getWeather"</code>, <code>parameters = { city: string(required) }</code>;</li>
<li class=""><code>HttpToolExecutor(baseUrl + "/v1/weather", GET, {Authorization=Bearer token}, {city → (QUERY, STRING, required)})</code>.</li>
</ul>
<p><strong>② Model-side function call</strong></p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"getWeather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"arguments"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"{\"city\":\"杭州\"}"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><code>arguments</code> is a <strong>JSON string</strong>, not an object.</p>
</blockquote>
<p><strong>③ Normalized into a request</strong></p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">ToolExecutionRequest{ id="call_1", name="getWeather", arguments="{\"city\":\"杭州\"}" }</span><br></div></code></pre></div></div>
<p><strong>④ ToolService locates the executor</strong></p>
<p><code>toolExecutors.get("getWeather")</code> → hits <code>HttpToolExecutor</code> (HTTP tools are <strong>not prefixed</strong>; the name is exactly <code>methodName</code>).</p>
<p><strong>⑤ Parameter processing (processArguments)</strong></p>
<table><thead><tr><th>Step</th><th>Input</th><th>Result</th></tr></thead><tbody><tr><td><code>argumentsAsMap</code></td><td><code>"{\"city\":\"杭州\"}"</code></td><td><code>{city: 杭州}</code></td></tr><tr><td>Take actual argument</td><td>key = <code>city</code></td><td><code>杭州</code></td></tr><tr><td>Required validation</td><td>REQUIRED</td><td>Pass</td></tr><tr><td>Type conversion</td><td>STRING</td><td><code>"杭州"</code></td></tr><tr><td>Location determination</td><td>QUERY, mapped name <code>q</code></td><td>To be appended to the URL</td></tr></tbody></table>
<p><strong>⑥ Assemble <code>HttpRequest</code></strong></p>
<ul>
<li class="">PATH: none; HEADER: none (the static <code>Authorization</code> is already in <code>staticHeaders</code>);</li>
<li class=""><code>shouldUseJsonBody</code>: GET → <code>false</code> (no request body sent);</li>
<li class=""><code>appendQueryParameters</code> → <code>?q=%E6%9D%AD%E5%B7%9E</code>;</li>
<li class="">Finally: <code>GET https://api.example.com/v1/weather?q=%E6%9D%AD%E5%B7%9E</code>, <code>body=""</code>.</li>
</ul>
<p><strong>⑦ Issue and retrieve the response</strong></p>
<p><code>JdkHttpTransport.execute(request)</code> → <code>200</code>, response body <code>{"city":"杭州","temp":26,"text":"晴"}</code>; <code>HttpToolExecutor.execute</code> returns the <strong>response body text</strong> directly.</p>
<p><strong>⑧ Feed back to the model</strong></p>
<p>The response body text is fed back as the tool result → the model produces its final answer accordingly.</p>
<p><strong>Run output (simulated terminal)</strong></p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">user</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain">  查一下杭州天气</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">think</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> 需要调用工具 getWeather</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">act</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain">   getWeather </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"city"</span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token string" style="color:rgb(255, 121, 198)">"杭州"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">http</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain">  GET https://api.example.com/v1/weather?q</span><span class="token operator">=</span><span class="token plain">%E6%9D%AD%E5%B7%9E → </span><span class="token number">200</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"city"</span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token string" style="color:rgb(255, 121, 198)">"杭州"</span><span class="token plain">,</span><span class="token string" style="color:rgb(255, 121, 198)">"temp"</span><span class="token plain">:26,</span><span class="token string" style="color:rgb(255, 121, 198)">"text"</span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token string" style="color:rgb(255, 121, 198)">"晴"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">think</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> 杭州当前 </span><span class="token number">26</span><span class="token plain">℃，天气晴。</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">final</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> 杭州当前 </span><span class="token number">26</span><span class="token plain">℃，天气晴。</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-verification-and-testing">5. Verification and Testing<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#5-verification-and-testing" class="hash-link" aria-label="Direct link to 5. Verification and Testing" title="Direct link to 5. Verification and Testing" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="51-unit-test-overview">5.1 Unit test overview<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#51-unit-test-overview" class="hash-link" aria-label="Direct link to 5.1 Unit test overview" title="Direct link to 5.1 Unit test overview" translate="no">​</a></h2>
<p>A <code>RecordingHttpTransport</code> records the received <code>HttpRequest</code>, asserting the URL / request headers / request body, with no real network required.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="52-test-list-current-implementation-all-passing">5.2 Test list (current implementation, all passing)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#52-test-list-current-implementation-all-passing" class="hash-link" aria-label="Direct link to 5.2 Test list (current implementation, all passing)" title="Direct link to 5.2 Test list (current implementation, all passing)" translate="no">​</a></h2>
<table><thead><tr><th>Case</th><th>Coverage</th></tr></thead><tbody><tr><td><code>shouldSendGetQueryParameters</code></td><td>QUERY parameter concatenation</td></tr><tr><td><code>shouldSubstitutePathParameters</code></td><td>PATH placeholder substitution</td></tr><tr><td><code>shouldSendJsonBodyForPost</code></td><td>BODY → JSON Body</td></tr><tr><td><code>shouldConvertArrayAndObjectBodyValues</code></td><td>ARRAY / OBJECT type conversion</td></tr><tr><td><code>shouldParseJsonEncodedArrayString</code></td><td>String array tolerance</td></tr><tr><td><code>shouldAddStaticAndDynamicHeaders</code></td><td>Static + HEADER parameters</td></tr><tr><td><code>shouldApplyDefaultValueWhenArgumentMissing</code></td><td>Default value</td></tr><tr><td><code>shouldThrowWhenRequiredArgumentMissing</code></td><td>Required validation</td></tr><tr><td><code>shouldRetryOnceOnConnectionResetForGet</code></td><td>Idempotent retry</td></tr><tr><td><code>shouldReturnFailureTextWhenPostFails</code></td><td>Failure-degradation text</td></tr><tr><td><code>shouldNotSendJsonBodyForGetEvenWithBodyConfig</code></td><td>GET does not send Body</td></tr><tr><td><code>HttpToolLoopTest#shouldCloseTheWeatherLoopThroughToolService</code></td><td><strong>End-to-end closure</strong>: plugin build → ToolService → function call → GET request → feed back (aligned with 4.4)</td></tr><tr><td><code>HttpToolFactoryTest</code></td><td>Spec / Executor construction, <code>IHttpPlugin</code>, null rejection</td></tr><tr><td><code>CurlParserConverterTest</code></td><td>curl parsing, bidirectional conversion, multi-method rejection</td></tr></tbody></table>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="53-boundaries-and-risks">5.3 Boundaries and risks<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#53-boundaries-and-risks" class="hash-link" aria-label="Direct link to 5.3 Boundaries and risks" title="Direct link to 5.3 Boundaries and risks" translate="no">​</a></h2>
<ul>
<li class=""><strong>Error semantics</strong>: <code>execute</code> degrades exceptions to text, with <code>isError=false</code>; structured errors must be wrapped by the caller.</li>
<li class=""><strong>Authentication complexity</strong>: Static headers / parameters suffice; OAuth refresh, signing, etc. must be implemented at the host layer.</li>
<li class=""><strong>Timeouts</strong>: Default connect 10 minutes, read 15 minutes (adjustable at construction time), rather lenient; tighten according to the scenario.</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-summary-and-outlook">6. Summary and Outlook<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#6-summary-and-outlook" class="hash-link" aria-label="Direct link to 6. Summary and Outlook" title="Direct link to 6. Summary and Outlook" translate="no">​</a></h2>
<ol>
<li class=""><strong>Essence</strong>: Declaratively map "a set of HTTP endpoints" into model-callable <code>ToolSpecification</code> + <code>ToolExecutor</code>.</li>
<li class=""><strong>Core</strong>: The four-dimensional parameter model (location × type × mapped name × constraint) + a fixed-order parameter pipeline.</li>
<li class=""><strong>Zero dependencies</strong>: Reuses the built-in <code>Json</code> and <code>JdkHttpTransport</code>, consistent with the Java 8 / zero-dependency constraint.</li>
<li class=""><strong>Unified</strong>: The output is, like the other modes, <code>Map&lt;ToolSpecification, ToolExecutor&gt;</code> that can coexist in the same table.</li>
<li class=""><strong>Outlook</strong>: Structured errors (<code>isError</code>), OpenAPI import, finer timeout / retry strategies.</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/http/%E9%80%82%E9%85%8DHTTP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20HTTP%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<p>[1]. <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods" target="_blank" rel="noopener noreferrer" class="">MDN HTTP Request Methods</a></p>
<p>[2]. <a href="https://json-schema.org/" target="_blank" rel="noopener noreferrer" class="">JSON Schema Official Site</a></p>
<p>[3]. <a href="https://curl.se/docs/manpage.html" target="_blank" rel="noopener noreferrer" class="">curl Command Manual</a></p>
<p>[4]. <a href="https://www.rfc-editor.org/rfc/rfc7231" target="_blank" rel="noopener noreferrer" class="">RFC 7231: HTTP/1.1 Semantics and Methods</a></p>
<p>[5]. <a href="https://spec.openapis.org/oas/latest.html" target="_blank" rel="noopener noreferrer" class="">OpenAPI Specification</a></p>
<p>[6]. Related internal documents: [Core Design Principles of the Local Tool Module](/blog/02、通用ReActagent层/functioncall核心扩展业务tool/local/Java代码转换FunctionCall协议01、AgentForge Local工具模块核心设计原理), <a class="" href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0">MCP Framework Research and AgentForge Integration Implementation</a>, [ChatModel Core Protocol Layer Design](/blog/01、model模型协议层/chatmodel/统一ChatModel协议层01、ChatModel 核心协议层设计：统一 API 契约封装与多协议扩展实现)</p>
<br>
<p>Compiler: changlu Created: 2026.10.5 Updated: 2026.10.5</p>]]></content:encoded>
            <category>AgentForge</category>
            <category>General ReAct Agent Layer</category>
            <category>Function Calling</category>
            <category>HTTP</category>
        </item>
        <item>
            <title><![CDATA[Java Code to FunctionCall Protocol 01, Core Design Principles of the AgentForge Local Tool Module]]></title>
            <link>https://agentforge-tech.github.io/AgentForge/en/blog/02、通用ReActagent层/functioncall核心扩展业务tool/local/Java代码转换FunctionCall协议01、AgentForge Local工具模块核心设计原理</link>
            <guid>https://agentforge-tech.github.io/AgentForge/en/blog/02、通用ReActagent层/functioncall核心扩展业务tool/local/Java代码转换FunctionCall协议01、AgentForge Local工具模块核心设计原理</guid>
            <pubDate>Wed, 07 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Updated: 2026-10-05]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>Updated: 2026-10-05<br>
<!-- -->Applicable module: <code>agentforge-agent-core</code> (<code>cloud.changlu.agentforge.agent.tool.local</code>)<br>
<!-- -->Dependent contracts: <code>agentforge-model-api</code> / <code>agentforge-model-core</code><br>
<!-- -->Maintainer: changlu</p>
</blockquote>
<!-- -->
<p>One-sentence conclusion: <strong>The Local tool module automatically scans methods annotated with <code>@Tool</code> on Java objects into <code>ToolSpecification</code>s, and uses reflection to bind the JSON arguments provided by the model to method parameters—implementing "let the model directly call your Java methods" with zero dependencies.</strong></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-background-and-problem-introduction">1. Background and Problem Introduction<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#1-background-and-problem-introduction" class="hash-link" aria-label="Direct link to 1. Background and Problem Introduction" title="Direct link to 1. Background and Problem Introduction" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="11-scenario-driven-conveniently-exposing-internal-methods-to-the-model">1.1 Scenario-driven: conveniently exposing internal methods to the model<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#11-scenario-driven-conveniently-exposing-internal-methods-to-the-model" class="hash-link" aria-label="Direct link to 1.1 Scenario-driven: conveniently exposing internal methods to the model" title="Direct link to 1.1 Scenario-driven: conveniently exposing internal methods to the model" translate="no">​</a></h2>
<p>While integrating AgentForge into internal CRM and ticketing systems, we encountered a very realistic scenario:</p>
<blockquote>
<p>The team already has a batch of ready-made Java methods such as <code>CrmService.queryCustomer(...)</code> and <code>TicketService.createTicket(...)</code>, and hopes to let the Agent call them with <strong>almost zero changes</strong>.</p>
</blockquote>
<p>This brought three concrete pain points:</p>
<ol>
<li class=""><strong>Manual registration</strong>: hand-writing a tool description and JSON Schema for every method—repetitive and error-prone;</li>
<li class=""><strong>Hard argument binding</strong>: the model supplies JSON, which must be manually parsed and type-converted (enums, arrays, numeric bounds, ...);</li>
<li class=""><strong>No heavy dependencies allowed</strong>: the core is Java 8 + zero third-party libraries, so Jackson / Gson cannot be used for deserialization.</li>
</ol>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="12-problem-led-how-to-turn-an-ordinary-java-method-into-a-tool-at-zero-cost">1.2 Problem-led: how to turn an "ordinary Java method" into a tool at zero cost?<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#12-problem-led-how-to-turn-an-ordinary-java-method-into-a-tool-at-zero-cost" class="hash-link" aria-label="Direct link to 1.2 Problem-led: how to turn an &quot;ordinary Java method&quot; into a tool at zero cost?" title="Direct link to 1.2 Problem-led: how to turn an &quot;ordinary Java method&quot; into a tool at zero cost?" translate="no">​</a></h2>
<blockquote>
<p><strong>Problem</strong>: How can an already-written Java method become a model-callable tool just by adding an annotation?</p>
</blockquote>
<p>Three things must be solved:</p>
<ol>
<li class=""><strong>Declaration</strong>: automatically generate a <code>ToolSpecification</code> (name, description, parameter Schema) from the method signature;</li>
<li class=""><strong>Binding</strong>: bind the <code>arguments</code> (JSON string) returned by the model to the method parameters;</li>
<li class=""><strong>Conversion</strong>: safely convert <code>Long</code> / <code>String</code> etc. in JSON into the <code>int</code> / <code>Enum</code> / <code>List</code> / <code>BigDecimal</code> expected by the method.</li>
</ol>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="13-design-goals-and-constraints">1.3 Design goals and constraints<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#13-design-goals-and-constraints" class="hash-link" aria-label="Direct link to 1.3 Design goals and constraints" title="Direct link to 1.3 Design goals and constraints" translate="no">​</a></h2>
<table><thead><tr><th>Goal</th><th>Description</th></tr></thead><tbody><tr><td>Annotation-driven</td><td>Add only <code>@Tool</code> / <code>@P</code>; do not change business logic</td></tr><tr><td>Strongly typed binding</td><td>Support enums, numeric bounds, BigDecimal / BigInteger, UUID, collections / Map</td></tr><tr><td>Zero dependencies</td><td>Reuse the built-in <code>Json</code>; do not pull in a JSON library</td></tr><tr><td>Unified</td><td>The output is <code>Map&lt;ToolSpecification, ToolExecutor&gt;</code>, the same tool table as http / mcp</td></tr><tr><td>Observable</td><td>Optional execution logging decorator</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-core-concepts">2. Core Concepts<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#2-core-concepts" class="hash-link" aria-label="Direct link to 2. Core Concepts" title="Direct link to 2. Core Concepts" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-two-annotations-tool-and-p">2.1 Two annotations: <code>@Tool</code> and <code>@P</code><a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#21-two-annotations-tool-and-p" class="hash-link" aria-label="Direct link to 21-two-annotations-tool-and-p" title="Direct link to 21-two-annotations-tool-and-p" translate="no">​</a></h2>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">name </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"getWeather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> value </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"查询城市天气"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">getWeather</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@P</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">name </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"city"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> description </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"城市名"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> city</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> weatherApi</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">query</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">city</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<ul>
<li class=""><code>@Tool</code>: marks the method as a tool; <code>name</code> defaults to the method name, <code>value</code> is the description;</li>
<li class=""><code>@P</code>: overrides the parameter name and description (when javac is not run with <code>-parameters</code>, reflection yields <code>arg0</code>, and this is the fallback).</li>
</ul>
<blockquote>
<p><strong>Key point</strong>: The parameter <strong>name</strong> determines the key in the model's JSON; if <code>@P</code> is absent and <code>-parameters</code> is off, the key becomes <code>arg0</code>, so always name parameters explicitly in production.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-generating-schema-via-reflection">2.2 Generating Schema via reflection<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#22-generating-schema-via-reflection" class="hash-link" aria-label="Direct link to 2.2 Generating Schema via reflection" title="Direct link to 2.2 Generating Schema via reflection" translate="no">​</a></h2>
<p><code>ToolSpecifications.toolSpecificationFrom(method)</code> derives it from the method signature:</p>
<table><thead><tr><th>Method element</th><th>Maps to</th></tr></thead><tbody><tr><td><code>@Tool.name</code> / method name</td><td><code>ToolSpecification.name</code></td></tr><tr><td><code>@Tool.value</code></td><td><code>ToolSpecification.description</code></td></tr><tr><td>Each parameter name (<code>@P.name</code> / reflected name)</td><td>JSON Schema <code>properties</code> key</td></tr><tr><td>Parameter type</td><td>JSON Schema <code>type</code> (string / integer / number / boolean / array / object)</td></tr><tr><td>Parameter</td><td><strong>Always required</strong> (the current implementation fixes it to <code>true</code>)</td></tr></tbody></table>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="23-localtoolexecutor-the-reflection-invoker">2.3 <code>LocalToolExecutor</code>: the reflection invoker<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#23-localtoolexecutor-the-reflection-invoker" class="hash-link" aria-label="Direct link to 23-localtoolexecutor-the-reflection-invoker" title="Direct link to 23-localtoolexecutor-the-reflection-invoker" translate="no">​</a></h2>
<p>It holds <strong>an object instance + a method</strong>, and on <code>execute</code> binds the arguments and then invokes reflectively:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">arguments(JSON) ──► LocalToolExecutionRequestUtil.argumentsAsMap</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                          │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                          ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">               prepareArguments(originalMethod, map)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                          每个参数：@P 名 → 取值 → coerceArgument 类型转换</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                          │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                          ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">               methodToInvoke.invoke(object, args)   （代理场景用原始方法取参数名）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                          │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                          ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">               返回值规范化 → String（回灌模型）</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="24-return-value-conventions-key">2.4 Return value conventions (key)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#24-return-value-conventions-key" class="hash-link" aria-label="Direct link to 2.4 Return value conventions (key)" title="Direct link to 2.4 Return value conventions (key)" translate="no">​</a></h2>
<table><thead><tr><th>Method return type</th><th>Content returned to the model</th></tr></thead><tbody><tr><td><code>void</code></td><td>The literal <code>"Success"</code></td></tr><tr><td><code>String</code></td><td>Returned as-is</td></tr><tr><td>Other types</td><td><code>Json.stringify(result)</code></td></tr></tbody></table>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="25-exception-strategy">2.5 Exception strategy<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#25-exception-strategy" class="hash-link" aria-label="Direct link to 2.5 Exception strategy" title="Direct link to 2.5 Exception strategy" translate="no">​</a></h2>
<p><code>LocalToolExecutor</code> provides two switches (both default to <code>false</code>):</p>
<table><thead><tr><th>Switch</th><th>Off (default)</th><th>On</th></tr></thead><tbody><tr><td><code>wrapToolArgumentsExceptions</code></td><td>Argument errors are thrown as-is</td><td>Wrapped as <code>ToolArgumentsException</code></td></tr><tr><td><code>propagateToolExecutionExceptions</code></td><td>Exceptions inside the method → return the exception message text</td><td>Throw <code>ToolExecutionException(cause)</code></td></tr></tbody></table>
<blockquote>
<p><strong>Note</strong>: By default, "method throws → the message is returned to the model as the result", which helps the model self-correct; enable <code>propagateToolExecutionExceptions</code> when strong failure semantics are needed.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-implementation-approach">3. Implementation Approach<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#3-implementation-approach" class="hash-link" aria-label="Direct link to 3. Implementation Approach" title="Direct link to 3. Implementation Approach" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-comparison-of-three-approaches">3.1 Comparison of three approaches<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#31-comparison-of-three-approaches" class="hash-link" aria-label="Direct link to 3.1 Comparison of three approaches" title="Direct link to 3.1 Comparison of three approaches" translate="no">​</a></h2>
<p><strong>Approach 1: Hand-write a <code>ToolExecutor</code> for every method</strong></p>
<ul>
<li class="">Pros: Fully controllable.</li>
<li class="">Drawbacks: A large amount of repetitive JSON parsing and type-conversion code, with high maintenance cost.</li>
</ul>
<p><strong>Approach 2: Introduce Jackson / Gson to deserialize directly into DTOs</strong></p>
<ul>
<li class="">Pros: Great developer experience.</li>
<li class="">Drawbacks: <strong>Breaks the core's zero-dependency constraint</strong> (Java 8, no third-party libraries), and is disconnected from AgentForge's built-in <code>Json</code> system.</li>
</ul>
<p><strong>Approach 3: Annotations + reflection + self-built type conversion (adopted by this module)</strong></p>
<ul>
<li class="">Pros: Zero dependencies, annotation-driven, cohesive argument binding and conversion, pluggable proxying/logging.</li>
<li class="">Drawbacks: Requires maintaining your own type-conversion matrix; complex generics (e.g. <code>List&lt;CustomDto&gt;</code>) need your own fallback.</li>
</ul>
<blockquote>
<p><strong>Conclusion</strong>: Adopt <strong>Approach 3</strong>—reuse the built-in <code>Json</code> and trade a testable type-conversion matrix for zero dependencies.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-scanning-and-table-building-flow">3.2 Scanning and table-building flow<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#32-scanning-and-table-building-flow" class="hash-link" aria-label="Direct link to 3.2 Scanning and table-building flow" title="Direct link to 3.2 Scanning and table-building flow" translate="no">​</a></h2>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">LocalToolFactory.buildLocalTools(objects)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   for each object:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">       拒绝 Class（必须是实例）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">       for each declaredMethod with @Tool:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">           spec = ToolSpecifications.toolSpecificationFrom(method)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">           重名 → 抛 IllegalArgumentException("Duplicated definition for tool: x")</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">           executor = new LocalToolExecutor(object, method)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                       （hasLogging=true 时再包一层 LoggingToolExecutor）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">           结果放入 Map&lt;ToolSpecification, ToolExecutor&gt;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   → 交给 ToolService 按 spec.name() 建表</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-argument-binding-pipeline">3.3 Argument binding pipeline<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#33-argument-binding-pipeline" class="hash-link" aria-label="Direct link to 3.3 Argument binding pipeline" title="Direct link to 3.3 Argument binding pipeline" translate="no">​</a></h2>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">参数为空? → 基本类型给默认值(0/false/'\0')，对象类型给 null</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">参数非空? → coerceArgument(值, 参数名, 目标Class, 泛型Type)</span><br></div></code></pre></div></div>
<p>Parameter name resolution priority: <code>@P.name()</code> → <code>parameter.getName()</code>.<br>
<strong>Proxy compatibility</strong>: AOP proxy methods lose parameter names, so the constructor supports <code>(object, originalMethod, methodToInvoke)</code>—use the original method to get parameter names and the proxy method to actually invoke.</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="34-type-conversion-matrix-core">3.4 Type-conversion matrix (core)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#34-type-conversion-matrix-core" class="hash-link" aria-label="Direct link to 3.4 Type-conversion matrix (core)" title="Direct link to 3.4 Type-conversion matrix (core)" translate="no">​</a></h2>
<p><code>coerceArgument</code> handles each target type in turn; out-of-range / invalid values error out directly:</p>
<table><thead><tr><th>Target type</th><th>Conversion strategy</th></tr></thead><tbody><tr><td><code>String</code></td><td><code>argument.toString()</code></td></tr><tr><td>Enum</td><td><code>Enum.valueOf</code>, then try <code>toUpperCase()</code> on failure</td></tr><tr><td><code>Boolean</code> / <code>boolean</code></td><td>Only accepts <code>Boolean</code>; otherwise errors</td></tr><tr><td><code>Double</code> / <code>Float</code></td><td>String or number → double; Float performs bounds checking</td></tr><tr><td><code>BigDecimal</code></td><td><code>BigDecimal.valueOf(double)</code></td></tr><tr><td><code>Integer</code> / <code>Long</code> / <code>Short</code> / <code>Byte</code></td><td>Uses <code>getBoundedLongValue</code> (non-fractional + bounds checking)</td></tr><tr><td><code>BigInteger</code></td><td>Non-fractional double → <code>BigDecimal.toBigInteger()</code></td></tr><tr><td><code>UUID</code></td><td><code>UUID.fromString</code></td></tr><tr><td><code>Collection</code> / <code>Map</code></td><td>Delegated to <code>LocalToolArgumentConverter</code> to parse JSON</td></tr></tbody></table>
<blockquote>
<p><strong>Key point</strong>: All numeric conversions go through a <strong>double intermediate state + bounds checking</strong> to avoid silent overflow; assigning a non-integer to an integer type errors out explicitly.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="35-complex-arguments-and-double-encoding-tolerance">3.5 Complex arguments and "double-encoding" tolerance<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#35-complex-arguments-and-double-encoding-tolerance" class="hash-link" aria-label="Direct link to 3.5 Complex arguments and &quot;double-encoding&quot; tolerance" title="Direct link to 3.5 Complex arguments and &quot;double-encoding&quot; tolerance" translate="no">​</a></h2>
<p>The model sometimes <strong>re-encodes an array / object as a string</strong> before passing it in (e.g. <code>"[1,2,3]"</code> or <code>"\"{...}\""</code>). <code>LocalToolArgumentConverter</code>'s strategy:</p>
<ol>
<li class="">If the argument is already an object → return as-is;</li>
<li class="">If it is a string → <code>Json.parse</code>; return if the type matches;</li>
<li class="">If it does not match → <code>cleanJsonString</code> (strip outer quotes, restore escapes) and retry;</li>
<li class="">If it still fails → throw <code>IllegalArgumentException</code>.</li>
</ol>
<p><code>LocalToolExecutionRequestUtil.argumentsAsMap</code> additionally tolerates: <strong>trailing commas</strong> and <strong>JSON wholly wrapped in quotes</strong>.</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="36-logging-decoration">3.6 Logging decoration<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#36-logging-decoration" class="hash-link" aria-label="Direct link to 3.6 Logging decoration" title="Direct link to 3.6 Logging decoration" translate="no">​</a></h2>
<p><code>LoggingToolExecutor</code> is a decorator of <code>ToolExecutor</code> that prints the tool name, input arguments, output and elapsed time via <code>java.util.logging</code> before and after execution. <code>buildLocalToolsWithLogging(objects, true)</code> enables it.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-practical-code">4. Practical Code<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#4-practical-code" class="hash-link" aria-label="Direct link to 4. Practical Code" title="Direct link to 4. Practical Code" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="41-defining-a-tool">4.1 Defining a tool<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#41-defining-a-tool" class="hash-link" aria-label="Direct link to 4.1 Defining a tool" title="Direct link to 4.1 Defining a tool" translate="no">​</a></h2>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">CrmTools</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">name </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"queryCustomer"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> value </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"按客户名查询客户信息"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">queryCustomer</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@P</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">name </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"name"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> description </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"客户名称"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@P</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">name </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"limit"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> description </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"返回条数"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">int</span><span class="token plain"> limit</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> crmService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">query</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> limit</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="42-registering-with-toolservice">4.2 Registering with ToolService<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#42-registering-with-toolservice" class="hash-link" aria-label="Direct link to 4.2 Registering with ToolService" title="Direct link to 4.2 Registering with ToolService" translate="no">​</a></h2>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ToolSpecification</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">ToolExecutor</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> tools </span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">LocalToolFactory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">buildLocalToolsWithLogging</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token class-name">Collections</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">singletonList</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">CrmTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ToolService</span><span class="token plain"> toolService </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">ToolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ReActAgent</span><span class="token plain"> agent </span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">ReActAgent</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">chatModel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">chatModel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p><strong>Run output (simulated terminal)</strong></p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># 1) 注册后模型可见的工具（tools[] 摘要）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">queryCustomer</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> 按客户名查询客户信息</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  parameters: </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> name: string </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">required</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">, limit: integer </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">required</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># 2) 打开 FINE 日志后，一次工具执行（LoggingToolExecutor 装饰）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">==</span><span class="token operator">=</span><span class="token plain"> 工具执行请求 </span><span class="token operator">==</span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">工具名称: queryCustomer</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">参数: </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"name"</span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token string" style="color:rgb(255, 121, 198)">"长路"</span><span class="token plain">,</span><span class="token string" style="color:rgb(255, 121, 198)">"limit"</span><span class="token plain">:5</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">记忆ID: session-1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">==</span><span class="token operator">=</span><span class="token plain"> 工具执行响应 </span><span class="token operator">==</span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">结果: </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"name"</span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token string" style="color:rgb(255, 121, 198)">"长路"</span><span class="token plain">,</span><span class="token string" style="color:rgb(255, 121, 198)">"level"</span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token string" style="color:rgb(255, 121, 198)">"VIP"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">耗时: </span><span class="token number">3</span><span class="token plain"> 毫秒</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">=</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="43-how-a-single-model-invocation-is-closed-out">4.3 How a single model invocation is closed out<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#43-how-a-single-model-invocation-is-closed-out" class="hash-link" aria-label="Direct link to 4.3 How a single model invocation is closed out" title="Direct link to 4.3 How a single model invocation is closed out" translate="no">​</a></h2>
<blockquote>
<p><strong>Corresponding unit test</strong>: <code>LocalToolLoopTest#shouldCloseTheCrmLoopThroughToolService</code>—runs the full offline path "scan → register → hit → bind → reflective invoke → feed back".</p>
</blockquote>
<p>Using <code>queryCustomer</code> from 4.1/4.2 as an example, breaking it down step by step:</p>
<p><strong>① Scan and registration (build time)</strong></p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ToolSpecification</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">ToolExecutor</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> tools </span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">LocalToolFactory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">buildLocalTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Collections</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">singletonList</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">CrmTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ToolService</span><span class="token plain"> toolService </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">ToolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(98, 114, 164)">// toolExecutors["queryCustomer"] = LocalToolExecutor(crmTools, method)</span><br></div></code></pre></div></div>
<ul>
<li class=""><code>ToolSpecifications.toolSpecificationFrom(method)</code> generates <code>ToolSpecification{ name=queryCustomer, parameters={ name:string(required), limit:integer(required) } }</code>;</li>
<li class=""><code>LocalToolExecutor</code> binds <strong>the object instance + method</strong> (for AOP proxies: use the original method for parameter names and the proxy method for the actual invocation).</li>
</ul>
<p><strong>② Model-side function call</strong></p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"queryCustomer"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"arguments"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"{\"name\":\"长路\",\"limit\":5}"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p><strong>③ Normalized into a request</strong></p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">ToolExecutionRequest{ id="call_1", name="queryCustomer", arguments="{\"name\":\"长路\",\"limit\":5}" }</span><br></div></code></pre></div></div>
<p><strong>④ ToolService locates the executor</strong></p>
<p><code>toolExecutors.get("queryCustomer")</code> → hits <code>LocalToolExecutor</code>.</p>
<p><strong>⑤ Argument binding (prepareArguments)</strong></p>
<table><thead><tr><th>Method parameter</th><th>Parameter name source</th><th>Value</th><th><code>coerceArgument</code></th></tr></thead><tbody><tr><td><code>String name</code></td><td><code>@P.name() = "name"</code></td><td><code>"长路"</code></td><td>STRING → <code>"长路"</code></td></tr><tr><td><code>int limit</code></td><td><code>@P.name() = "limit"</code></td><td><code>5</code> (Json parses as a number)</td><td>double intermediate + non-fractional + bounds check → <code>(int) 5</code></td></tr></tbody></table>
<blockquote>
<p><strong>Note</strong>: When an argument is missing, primitive types get their defaults (<code>int → 0</code>, <code>boolean → false</code>) and object types get <code>null</code>; the parameter name is taken from <code>@P.name()</code> first, otherwise from the reflected parameter name.</p>
</blockquote>
<p><strong>⑥ Reflective invocation and return normalization</strong></p>
<p><code>methodToInvoke.invoke(crmTools, ["长路", 5])</code>; the return value is handled by convention: <code>String</code> as-is, <code>void</code> → <code>"Success"</code>, others → <code>Json.stringify</code>.</p>
<p><strong>⑦ Feed back to the model</strong></p>
<p>The tool return value is fed back as the result → the model answers accordingly.</p>
<p><strong>Run output (simulated terminal)</strong></p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">user</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain">  查一下客户</span><span class="token string" style="color:rgb(255, 121, 198)">"长路"</span><span class="token plain">的信息</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">think</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> 调用 queryCustomer</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">act</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain">   queryCustomer </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"name"</span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token string" style="color:rgb(255, 121, 198)">"长路"</span><span class="token plain">,</span><span class="token string" style="color:rgb(255, 121, 198)">"limit"</span><span class="token plain">:5</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">reflect</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> CrmTools.queryCustomer</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"长路"</span><span class="token plain">, </span><span class="token number">5</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> → customer:长路,limit:5</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">think</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> 已查询到客户长路。</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">final</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> 已查询到客户长路。</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-verification-and-testing">5. Verification and Testing<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#5-verification-and-testing" class="hash-link" aria-label="Direct link to 5. Verification and Testing" title="Direct link to 5. Verification and Testing" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="51-unit-test-overview">5.1 Unit test overview<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#51-unit-test-overview" class="hash-link" aria-label="Direct link to 5.1 Unit test overview" title="Direct link to 5.1 Unit test overview" translate="no">​</a></h2>
<p><code>LocalToolFactoryTest</code> covers factory scanning, logging decoration, and argument-tool tolerance; <code>LocalToolExecutorTest</code> covers argument binding and the type-conversion matrix. All run offline.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="52-test-list-current-implementation-all-passing">5.2 Test list (current implementation, all passing)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#52-test-list-current-implementation-all-passing" class="hash-link" aria-label="Direct link to 5.2 Test list (current implementation, all passing)" title="Direct link to 5.2 Test list (current implementation, all passing)" translate="no">​</a></h2>
<table><thead><tr><th>Case</th><th>Coverage</th></tr></thead><tbody><tr><td><code>shouldBuildToolsFromAnnotatedObject</code></td><td>Scans <code>@Tool</code> to build the mapping</td></tr><tr><td><code>shouldDecorateExecutorWithLogging</code></td><td>Logging decorator</td></tr><tr><td><code>shouldReturnEmptyMapForNullOrEmptyInput</code></td><td>Empty input boundary</td></tr><tr><td><code>shouldBindStringArgumentUsingPAnnotation</code></td><td><code>@P</code> parameter name binding</td></tr><tr><td><code>shouldCoerceNumericArguments</code></td><td>Numeric type conversion</td></tr><tr><td><code>shouldUsePrimitiveDefaultWhenArgumentMissing</code></td><td>Primitive defaults</td></tr><tr><td><code>shouldCoerceEnumNameIgnoringCase</code></td><td>Enum case-insensitivity</td></tr><tr><td><code>shouldConvertJsonStringIntoListCollection</code></td><td>String → List</td></tr><tr><td><code>shouldPassThroughNativeList</code></td><td>Native List pass-through</td></tr><tr><td><code>shouldConvertJsonStringIntoMap</code></td><td>String → Map</td></tr><tr><td><code>shouldReturnSuccessForVoidMethod</code></td><td>void → <code>"Success"</code></td></tr><tr><td><code>shouldSerializeNonStringReturnValueAsJson</code></td><td>Non-String return → JSON</td></tr><tr><td><code>shouldReturnThrownMessageByDefault</code></td><td>Default exception-degradation text</td></tr><tr><td><code>shouldThrowWrappedExecutionExceptionWhenPropagating</code></td><td>Exception propagation switch</td></tr><tr><td><code>shouldWrapArgumentsExceptionWhenConfigured</code></td><td>Argument exception wrapping switch</td></tr><tr><td><code>shouldTolerateTrailingCommaWhenParsingArguments</code></td><td>Trailing comma tolerance</td></tr><tr><td><code>shouldConvertDoubleEncodedJsonString</code></td><td>Double-encoded JSON tolerance</td></tr><tr><td><code>LocalToolLoopTest#shouldCloseTheCrmLoopThroughToolService</code></td><td><strong>End-to-end closure</strong>: <code>@Tool</code> scan → ToolService → function call → argument binding → reflective invoke → feed back (aligned with 4.3)</td></tr></tbody></table>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="53-boundaries-and-risks">5.3 Boundaries and risks<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#53-boundaries-and-risks" class="hash-link" aria-label="Direct link to 5.3 Boundaries and risks" title="Direct link to 5.3 Boundaries and risks" translate="no">​</a></h2>
<ul>
<li class=""><strong>Parameter name dependency</strong>: Without <code>-parameters</code> and without <code>@P</code>, the model sees <code>arg0</code>; always name explicitly.</li>
<li class=""><strong>required fixed to true</strong>: All parameters are currently mandatory; optional parameters need upper-layer fallback or a default value.</li>
<li class=""><strong>Generic erasure</strong>: <code>List&lt;CustomDto&gt;</code> can only be parsed into a <code>Map</code> / <code>List</code> structure and cannot be automatically instantiated into a concrete type.</li>
<li class=""><strong>Strict booleans</strong>: A <code>Boolean</code> field accepts only boolean values; the string <code>"true"</code> is not implicitly converted.</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-summary-and-outlook">6. Summary and Outlook<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#6-summary-and-outlook" class="hash-link" aria-label="Direct link to 6. Summary and Outlook" title="Direct link to 6. Summary and Outlook" translate="no">​</a></h2>
<ol>
<li class=""><strong>Essence</strong>: <code>@Tool</code> annotation + reflection + type conversion turn ordinary Java methods into model-callable tools.</li>
<li class=""><strong>Core</strong>: <code>ToolSpecifications</code> generates the Schema; <code>LocalToolExecutor</code> binds arguments and invokes reflectively.</li>
<li class=""><strong>Zero dependencies</strong>: Reuses the built-in <code>Json</code>, consistent with the Java 8 / zero-third-party-library constraint.</li>
<li class=""><strong>Engineering</strong>: Configurable exception strategy, decoratable logging, proxy compatible.</li>
<li class=""><strong>Outlook</strong>: Optional parameters (controllable required), generic DTO deserialization, SpEL / expression default values.</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/local/Java%E4%BB%A3%E7%A0%81%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE01%E3%80%81AgentForge%20Local%E5%B7%A5%E5%85%B7%E6%A8%A1%E5%9D%97%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E5%8E%9F%E7%90%86#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<p>[1]. <a href="https://docs.oracle.com/javase/tutorial/reflect/" target="_blank" rel="noopener noreferrer" class="">Java Reflection Tutorial (Oracle)</a></p>
<p>[2]. <a href="https://json-schema.org/" target="_blank" rel="noopener noreferrer" class="">JSON Schema Official Site</a></p>
<p>[3]. <a href="https://docs.oracle.com/javase/tutorial/java/annotations/" target="_blank" rel="noopener noreferrer" class="">Java Annotations Tutorial (Oracle)</a></p>
<p>[4]. Related internal documents: [Core Design Principles of the HTTP Tool Module](/blog/02、通用ReActagent层/functioncall核心扩展业务tool/http/适配HTTP协议转换FunctionCall协议01、AgentForge HTTP工具模块核心设计原理), <a class="" href="https://agentforge-tech.github.io/AgentForge/en/blog/02%E3%80%81%E9%80%9A%E7%94%A8ReActagent%E5%B1%82/functioncall%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E4%B8%9A%E5%8A%A1tool/mcp/MCP%E5%8D%8F%E8%AE%AE%E8%BD%AC%E6%8D%A2FunctionCall%E5%8D%8F%E8%AE%AE02%E3%80%81MCP%E6%A1%86%E6%9E%B6%E8%B0%83%E7%A0%94%E4%B8%8EAgentForge%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0">MCP Framework Research and AgentForge Integration Implementation</a></p>
<br>
<p>Compiler: changlu Created: 2026.10.5 Updated: 2026.10.5</p>]]></content:encoded>
            <category>AgentForge</category>
            <category>General ReAct Agent Layer</category>
            <category>Function Calling</category>
            <category>Local</category>
        </item>
        <item>
            <title><![CDATA[Anthropic Protocol 02, Core Practices for AgentForge's Anthropic Integration]]></title>
            <link>https://agentforge-tech.github.io/AgentForge/en/blog/01、model模型协议层/chatmodel/anthropic/Anthropic协议02、AgentForge 适配Anthropic接入核心实践</link>
            <guid>https://agentforge-tech.github.io/AgentForge/en/blog/01、model模型协议层/chatmodel/anthropic/Anthropic协议02、AgentForge 适配Anthropic接入核心实践</guid>
            <pubDate>Tue, 06 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Updated: 2026-10-05]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>Updated: 2026-10-05<br>
<!-- -->Applicable module: <code>agentforge-model-anthropic</code><br>
<!-- -->Current implementation: <code>AnthropicChatModel</code> (Blocking), <code>AnthropicStreamingChatModel</code> (SSE), <code>AnthropicProtocol</code> (shared wire mapping)<br>
<!-- -->Wire API: Anthropic Messages API<br>
<!-- -->Maintainer: changlu</p>
</blockquote>
<!-- -->
<p>One-sentence conclusion: The Anthropic Provider bidirectionally maps AgentForge's unified <code>ChatRequest</code> / <code>ChatResponse</code> to the <strong>Messages API</strong>, and handles Anthropic's two peculiar differences — "top-level system field + content block"; for the protocol itself, see "Anthropic Protocol 01, A Quick Understanding of the Anthropic Low-Level Protocol".</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="i-background-and-problem-introduction">I. Background and Problem Introduction<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#i-background-and-problem-introduction" class="hash-link" aria-label="Direct link to I. Background and Problem Introduction" title="Direct link to I. Background and Problem Introduction" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="11-scenario-driven-one-core-adapting-to-claudes-block-protocol">1.1, Scenario-Driven: One Core, adapting to Claude's block protocol<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#11-scenario-driven-one-core-adapting-to-claudes-block-protocol" class="hash-link" aria-label="Direct link to 1.1, Scenario-Driven: One Core, adapting to Claude's block protocol" title="Direct link to 1.1, Scenario-Driven: One Core, adapting to Claude's block protocol" translate="no">​</a></h2>
<blockquote>
<p>AgentForge's upper layers depend only on <code>ChatMessage</code> / <code>ChatRequest</code> / <code>ChatResponse</code>. When integrating Claude, wire details such as the "top-level system field", "content block", and "tool_use/tool_result" must not leak into the Agent Runtime.</p>
</blockquote>
<p>Therefore the Anthropic Provider's responsibility is to be a <strong>protocol translation layer</strong>, and to let Blocking and Streaming share the same set of mapping logic.</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="12-problem-guidance-how-does-the-block-protocol-map-to-unified-messages">1.2, Problem Guidance: How does the block protocol map to unified messages?<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#12-problem-guidance-how-does-the-block-protocol-map-to-unified-messages" class="hash-link" aria-label="Direct link to 1.2, Problem Guidance: How does the block protocol map to unified messages?" title="Direct link to 1.2, Problem Guidance: How does the block protocol map to unified messages?" translate="no">​</a></h2>
<blockquote>
<p><strong>Question</strong>: How does <code>SystemMessage</code> map to the top-level <code>system</code>? How do <code>AiMessage</code>'s text and tool calls map to <code>content[]</code>? How does <code>ToolExecutionResultMessage</code> map to <code>tool_result</code>? How are streaming <code>input_json_delta</code>s aggregated into complete tool arguments?</p>
</blockquote>
<p>This article provides the mapping and implementation location for each item.</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="13-builder-parameters">1.3, Builder parameters<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#13-builder-parameters" class="hash-link" aria-label="Direct link to 1.3, Builder parameters" title="Direct link to 1.3, Builder parameters" translate="no">​</a></h2>
<table><thead><tr><th>Builder parameter</th><th>Default</th><th>Description</th></tr></thead><tbody><tr><td><code>baseUrl</code></td><td><code>https://api.anthropic.com</code></td><td>API root address</td></tr><tr><td><code>apiKey</code></td><td><code>null</code></td><td>Required, written to <code>x-api-key</code></td></tr><tr><td><code>anthropicVersion</code></td><td><code>2023-06-01</code></td><td>Written to the <code>anthropic-version</code> Header</td></tr><tr><td><code>modelName</code></td><td><code>null</code></td><td>Must have a value before the request</td></tr><tr><td><code>temperature</code></td><td><code>null</code></td><td>Non-null maps to <code>temperature</code> (compatibility see 5.4)</td></tr><tr><td><code>maxTokens</code></td><td><code>1024</code></td><td>Maps to the required <code>max_tokens</code></td></tr><tr><td><code>topP</code></td><td><code>null</code></td><td>Non-null maps to <code>top_p</code> (compatibility see 5.4)</td></tr><tr><td><code>stopSequences</code></td><td><code>null</code></td><td>Maps to <code>stop_sequences</code></td></tr><tr><td><code>customParameter</code></td><td>empty</td><td>Provider top-level extension fields</td></tr><tr><td><code>customHeader</code></td><td>empty</td><td>Custom Header</td></tr><tr><td><code>httpTransport</code></td><td><code>JdkHttpTransport</code></td><td>HTTP SPI</td></tr><tr><td><code>connectTimeoutMillis</code></td><td><code>10000</code></td><td>Connect timeout</td></tr><tr><td><code>readTimeoutMillis</code></td><td><code>60000</code></td><td>Read timeout</td></tr></tbody></table>
<blockquote>
<p><strong>Key point</strong>: <code>max_tokens</code> is a required field for Anthropic; AgentForge defaults to 1024 to ensure the simplest call can execute directly.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ii-core-concepts">II. Core Concepts<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#ii-core-concepts" class="hash-link" aria-label="Direct link to II. Core Concepts" title="Direct link to II. Core Concepts" translate="no">​</a></h2>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">ChatRequest</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token class-name">List</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ChatMessage</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> messages</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token class-name">ChatRequestParameters</span><span class="token plain"> parameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ChatResponse</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token class-name">AiMessage</span><span class="token plain"> aiMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token class-name">TokenUsage</span><span class="token plain"> tokenUsage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token class-name">FinishReason</span><span class="token plain"> finishReason</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics class-name">Object</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The Provider is responsible for translating Core's <code>ChatRequest</code> into the Anthropic wire, and then normalizing the response into <code>ChatResponse</code>.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="iii-implementation-approach-and-mapping">III. Implementation Approach and Mapping<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#iii-implementation-approach-and-mapping" class="hash-link" aria-label="Direct link to III. Implementation Approach and Mapping" title="Direct link to III. Implementation Approach and Mapping" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-http-headers">3.1, HTTP Headers<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#31-http-headers" class="hash-link" aria-label="Direct link to 3.1, HTTP Headers" title="Direct link to 3.1, HTTP Headers" translate="no">​</a></h2>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Content-Type: application/json</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Accept: application/json</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">x-api-key: ${ANTHROPIC_API_KEY}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">anthropic-version: 2023-06-01</span><br></div></code></pre></div></div>
<p>Then append <code>customHeaders</code>.</p>
<blockquote>
<p><strong>Note</strong>: It is not <code>Authorization: Bearer</code>, and <code>anthropic-version</code> must be sent explicitly.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-systemmessage-mapping">3.2, SystemMessage mapping<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#32-systemmessage-mapping" class="hash-link" aria-label="Direct link to 3.2, SystemMessage mapping" title="Direct link to 3.2, SystemMessage mapping" translate="no">​</a></h2>
<p>Anthropic's system prompt uses a <strong>top-level <code>system</code> field</strong>. AgentForge iterates over all <code>SystemMessage</code>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">SystemMessage A + SystemMessage B → "A\n\nB" → {"system":"A\n\nB"}</span><br></div></code></pre></div></div>
<p>The concatenated system <strong>no longer enters the <code>messages</code> array</strong>.</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-user--ai-message-mapping">3.3, User / AI message mapping<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#33-user--ai-message-mapping" class="hash-link" aria-label="Direct link to 3.3, User / AI message mapping" title="Direct link to 3.3, User / AI message mapping" translate="no">​</a></h2>
<table><thead><tr><th>AgentForge</th><th>Anthropic role</th><th>content</th></tr></thead><tbody><tr><td><code>UserMessage</code> (single text)</td><td><code>user</code></td><td><code>message.text()</code> (string)</td></tr><tr><td><code>UserMessage</code> (multiple <code>Content</code>)</td><td><code>user</code></td><td><code>content[]</code>, each <code>TextContent</code> → <code>{"type":"text","text":...}</code></td></tr><tr><td><code>AiMessage</code> (plain text)</td><td><code>assistant</code></td><td><code>message.text()</code> (string)</td></tr><tr><td><code>AiMessage</code> (with tool calls)</td><td><code>assistant</code></td><td><code>content[]</code>: optional text block + <code>tool_use</code> block</td></tr><tr><td><code>ToolExecutionResultMessage</code></td><td><code>user</code></td><td><code>content[]</code>: <code>tool_result</code> block</td></tr><tr><td><code>SystemMessage</code></td><td>Does not enter messages</td><td>Aggregated into the top-level <code>system</code></td></tr></tbody></table>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="34-tool-call-mapping">3.4, Tool call mapping<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#34-tool-call-mapping" class="hash-link" aria-label="Direct link to 3.4, Tool call mapping" title="Direct link to 3.4, Tool call mapping" translate="no">​</a></h2>
<p>When <code>AiMessage.hasToolExecutionRequests()</code> is true, it maps to the <code>assistant</code>'s <code>content[]</code>:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"text"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"text"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"I will check."</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tool_use"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"toolu_1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"input"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"city"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"hangzhou"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p><code>ToolExecutionResultMessage</code> maps to the <code>user</code>'s <code>content[]</code>:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"user"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tool_result"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"tool_use_id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"toolu_1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"{\"temperature\":22}"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<table><thead><tr><th>Core field</th><th>Anthropic field</th></tr></thead><tbody><tr><td><code>ToolExecutionRequest.id</code></td><td><code>tool_use.id</code> (written back to the corresponding <code>tool_result.tool_use_id</code>)</td></tr><tr><td><code>ToolExecutionRequest.name</code></td><td><code>tool_use.name</code></td></tr><tr><td><code>ToolExecutionRequest.arguments</code> (JSON string)</td><td><code>tool_use.input</code> (sent after being <strong>parsed into an object</strong>)</td></tr><tr><td><code>AiMessage.text</code></td><td>Optional preceding <code>text</code> block</td></tr><tr><td><code>ToolExecutionResultMessage.isError == true</code></td><td><code>tool_result.is_error = true</code></td></tr></tbody></table>
<blockquote>
<p><strong>Note</strong>: <code>CustomMessage</code> has no <code>text()</code>, and currently still throws <code>UnsupportedOperationException</code>.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="35-parameter-mapping">3.5, Parameter mapping<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#35-parameter-mapping" class="hash-link" aria-label="Direct link to 3.5, Parameter mapping" title="Direct link to 3.5, Parameter mapping" translate="no">​</a></h2>
<table><thead><tr><th>AgentForge parameter</th><th>Anthropic field</th><th>Current behavior</th></tr></thead><tbody><tr><td><code>modelName</code></td><td><code>model</code></td><td>Required; fails locally if empty</td></tr><tr><td><code>maxTokens</code></td><td><code>max_tokens</code></td><td>Required; defaults to 1024</td></tr><tr><td><code>temperature</code></td><td><code>temperature</code></td><td>Sent if non-null; note new-model compatibility</td></tr><tr><td><code>topP</code></td><td><code>top_p</code></td><td>Sent if non-null; note new-model compatibility</td></tr><tr><td><code>stopSequences</code></td><td><code>stop_sequences</code></td><td>Sent if non-null</td></tr><tr><td><code>tools</code></td><td><code>tools[]</code></td><td><code>{"name","description","input_schema"}</code>; sends an empty object schema when there are no parameters</td></tr><tr><td><code>toolChoice</code></td><td><code>tool_choice</code></td><td><code>AUTO→{"type":"auto"}</code>、<code>NONE→{"type":"none"}</code>、<code>REQUIRED→{"type":"any"}</code>、<code>SPECIFIC→{"type":"tool","name":X}</code></td></tr><tr><td><code>SystemMessage</code></td><td><code>system</code></td><td>Multiple concatenated with a double newline</td></tr><tr><td><code>customParameters</code></td><td>Top-level pass-through</td><td>Standard fields ultimately override custom values of the same name</td></tr></tbody></table>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="36-response-extraction-rules">3.6, Response extraction rules<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#36-response-extraction-rules" class="hash-link" aria-label="Direct link to 3.6, Response extraction rules" title="Direct link to 3.6, Response extraction rules" translate="no">​</a></h2>
<p>Iterate over the <code>content</code> array:</p>
<ul>
<li class="">Only extract blocks with <code>type == "text"</code> and concatenate them in order into <code>AiMessage.text()</code>;</li>
<li class="">Extract blocks with <code>type == "tool_use"</code> and map them to <code>ToolExecutionRequest</code>:</li>
</ul>
<table><thead><tr><th>Anthropic <code>tool_use</code></th><th>AgentForge</th></tr></thead><tbody><tr><td><code>id</code></td><td><code>id</code></td></tr><tr><td><code>name</code></td><td><code>name</code></td></tr><tr><td><code>input</code> (object)</td><td><code>arguments</code> (<code>Json.stringify(input)</code>)</td></tr></tbody></table>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">content = [text("I will check."), tool_use(...)]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ↓</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">AiMessage.text()                     = "I will check."</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">AiMessage.hasToolExecutionRequests() = true</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">AiMessage.toolExecutionRequests()    = [ToolExecutionRequest(toolu_1, get_weather, {"city":"hangzhou"})]</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Note</strong>: When there is only a <code>tool_use</code> and no text block, <code>AiMessage.text()</code> is <code>null</code>; <code>thinking</code> / <code>image</code> / <code>document</code> / server tool blocks currently do not enter <code>AiMessage</code>.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="37-chatresponse-mapping">3.7, ChatResponse mapping<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#37-chatresponse-mapping" class="hash-link" aria-label="Direct link to 3.7, ChatResponse mapping" title="Direct link to 3.7, ChatResponse mapping" translate="no">​</a></h2>
<table><thead><tr><th>Anthropic field</th><th>AgentForge field</th></tr></thead><tbody><tr><td><code>content[*].text</code></td><td><code>ChatResponse.aiMessage().text()</code></td></tr><tr><td><code>content[*].tool_use</code></td><td><code>ChatResponse.aiMessage().toolExecutionRequests()</code></td></tr><tr><td><code>usage.input_tokens</code></td><td><code>TokenUsage.inputTokens()</code></td></tr><tr><td><code>usage.output_tokens</code></td><td><code>TokenUsage.outputTokens()</code></td></tr><tr><td><code>input + output</code></td><td><code>TokenUsage.totalTokens()</code></td></tr><tr><td><code>stop_reason</code></td><td><code>ChatResponse.finishReason()</code></td></tr><tr><td><code>id</code> / <code>model</code> / <code>type</code></td><td><code>metadata["id"] / ["model"] / ["type"]</code></td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="38-stop_reason-mapping">3.8, stop_reason mapping<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#38-stop_reason-mapping" class="hash-link" aria-label="Direct link to 3.8, stop_reason mapping" title="Direct link to 3.8, stop_reason mapping" translate="no">​</a></h2>
<table><thead><tr><th>Anthropic</th><th>AgentForge <code>FinishReason</code></th></tr></thead><tbody><tr><td><code>end_turn</code></td><td><code>STOP</code></td></tr><tr><td><code>stop_sequence</code></td><td><code>STOP</code></td></tr><tr><td><code>max_tokens</code></td><td><code>LENGTH</code></td></tr><tr><td><code>tool_use</code></td><td><code>TOOL_EXECUTION</code></td></tr><tr><td>Other non-null</td><td><code>OTHER</code></td></tr><tr><td><code>null</code></td><td><code>null</code></td></tr></tbody></table>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="39-streaming-implementation">3.9, Streaming implementation<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#39-streaming-implementation" class="hash-link" aria-label="Direct link to 3.9, Streaming implementation" title="Direct link to 3.9, Streaming implementation" translate="no">​</a></h2>
<p><code>AnthropicStreamingChatModel</code> uses the same Endpoint, forces <code>stream=true</code>, and <code>Accept: text/event-stream</code>; the request body mapping is exactly the same as Blocking, uniformly generated by <code>AnthropicProtocol</code>.</p>
<p><code>JdkHttpTransport</code> only calls back line by line and does not parse SSE semantics; <code>AnthropicStreamState</code> itself accumulates one frame by <code>event:</code> / <code>data:</code>, and dispatches on encountering an empty line (or a single-line <code>data:</code> frame):</p>
<table><thead><tr><th>Event</th><th>AgentForge behavior</th></tr></thead><tbody><tr><td><code>message_start</code></td><td>Records <code>metadata["id"]/["model"]</code>, reads <code>usage.input_tokens</code></td></tr><tr><td><code>content_block_start</code></td><td>For <code>tool_use</code>, creates an accumulator by <code>index</code>, recording <code>id</code> / <code>name</code> / initial <code>input</code></td></tr><tr><td><code>content_block_delta</code></td><td><code>text_delta</code> immediately <code>onPartialResponse(...)</code>; <code>input_json_delta</code> appended to the tool arguments of the same <code>index</code></td></tr><tr><td><code>content_block_stop</code></td><td>Not processed, waits for the whole block to end</td></tr><tr><td><code>message_delta</code></td><td>Reads <code>delta.stop_reason</code> and <code>usage.output_tokens</code></td></tr><tr><td><code>message_stop</code></td><td>Triggers the final response via EOF / completion callback</td></tr><tr><td><code>error</code></td><td>Wrapped as <code>ModelException</code> and <code>onError(...)</code></td></tr></tbody></table>
<p>Tool call aggregation rules:</p>
<ul>
<li class="">Text blocks and tool blocks can interleave and do not affect each other;</li>
<li class=""><code>arguments</code> concatenates the raw JSON text in arrival order, without reformatting;</li>
<li class="">If there is only <code>content_block_start</code> and no <code>input_json_delta</code> (<code>input</code> directly given as an object), use the start frame's <code>input</code>;</li>
<li class="">When there is no <code>partial_json</code> at all, <code>arguments</code> is normalized to <code>{}</code>;</li>
<li class="">Consistent with LangChain4j: during streaming, <strong>do not</strong> expose half-finished tool calls; only the final <code>ChatResponse</code> gives the complete result.</li>
</ul>
<blockquote>
<p><strong>Key point</strong>: <code>temperature</code> / <code>top_p</code> have been officially marked by Anthropic as deprecated/restricted for newer models; it is recommended not to set them explicitly by default, and to decide whether to send them based on the target model's capabilities. The Provider may later add model capability validation to block invalid parameters on the client side in advance.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="iv-practical-code">IV. Practical Code<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#iv-practical-code" class="hash-link" aria-label="Direct link to IV. Practical Code" title="Direct link to IV. Practical Code" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="41-non-streaming">4.1, Non-streaming<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#41-non-streaming" class="hash-link" aria-label="Direct link to 4.1, Non-streaming" title="Direct link to 4.1, Non-streaming" translate="no">​</a></h2>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">AnthropicChatModel</span><span class="token plain"> model </span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">AnthropicChatModel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">baseUrl</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"https://api.anthropic.com"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">apiKey</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getenv</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"ANTHROPIC_API_KEY"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">modelName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"your-claude-model"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">maxTokens</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">1024</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ChatRequest</span><span class="token plain"> request </span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">ChatRequest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">SystemMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"You are a concise Java assistant."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">UserMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Explain volatile."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ChatResponse</span><span class="token plain"> response </span><span class="token operator">=</span><span class="token plain"> model</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">chat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">aiMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">text</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p><strong>Run output (simulated terminal)</strong></p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">$ </span><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-s</span><span class="token plain"> https://api.anthropic.com/v1/messages </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"x-api-key: </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$ANTHROPIC_API_KEY</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"anthropic-version: 2023-06-01"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-d</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'{"model":"your-claude-model","max_tokens":1024,"system":"You are a concise Java assistant.","messages":[{"role":"user","content":"Explain volatile."}]}'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"id"</span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token string" style="color:rgb(255, 121, 198)">"msg_xxx"</span><span class="token plain">,</span><span class="token string" style="color:rgb(255, 121, 198)">"type"</span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token string" style="color:rgb(255, 121, 198)">"message"</span><span class="token plain">,</span><span class="token string" style="color:rgb(255, 121, 198)">"role"</span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token plain">,</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"content"</span><span class="token plain">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"type"</span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token string" style="color:rgb(255, 121, 198)">"text"</span><span class="token plain">,</span><span class="token string" style="color:rgb(255, 121, 198)">"text"</span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token string" style="color:rgb(255, 121, 198)">"volatile provides visibility guarantees..."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain">,</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"stop_reason"</span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token string" style="color:rgb(255, 121, 198)">"end_turn"</span><span class="token plain">,</span><span class="token string" style="color:rgb(255, 121, 198)">"usage"</span><span class="token plain">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"input_tokens"</span><span class="token plain">:24,</span><span class="token string" style="color:rgb(255, 121, 198)">"output_tokens"</span><span class="token plain">:40</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="42-tool-call-closed-loop-wire-example">4.2, Tool call closed loop (wire example)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#42-tool-call-closed-loop-wire-example" class="hash-link" aria-label="Direct link to 4.2, Tool call closed loop (wire example)" title="Direct link to 4.2, Tool call closed loop (wire example)" translate="no">​</a></h2>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// assistant initiates</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"role"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"content"</span><span class="token operator">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"text"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"text"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"I will check."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"tool_use"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"toolu_1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"input"</span><span class="token operator">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"city"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"hangzhou"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// tool result write-back</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"role"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"user"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"content"</span><span class="token operator">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"tool_result"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"tool_use_id"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"toolu_1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token property">"content"</span><span class="token operator">:</span><span class="token string" style="color:rgb(255, 121, 198)">"{\"temperature\":22}"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="v-error-handling-limitations-and-evolution">V. Error Handling, Limitations and Evolution<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#v-error-handling-limitations-and-evolution" class="hash-link" aria-label="Direct link to V. Error Handling, Limitations and Evolution" title="Direct link to V. Error Handling, Limitations and Evolution" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="51-local-parameter-validation">5.1, Local parameter validation<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#51-local-parameter-validation" class="hash-link" aria-label="Direct link to 5.1, Local parameter validation" title="Direct link to 5.1, Local parameter validation" translate="no">​</a></h2>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">modelName must be non-empty</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">apiKey must be non-empty</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">messages must contain at least one non-system user/assistant message</span><br></div></code></pre></div></div>
<p>When there is only a <code>SystemMessage</code>, it throws:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">IllegalArgumentException: Anthropic request requires at least one user/assistant message</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="52-http-errors">5.2, HTTP errors<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#52-http-errors" class="hash-link" aria-label="Direct link to 5.2, HTTP errors" title="Direct link to 5.2, HTTP errors" translate="no">​</a></h2>
<p>Non-2xx → <code>new ModelException("Anthropic request failed with HTTP " + statusCode, statusCode, responseBody)</code>; IOException → <code>ModelException("Anthropic request failed", cause)</code>. Upper layers can uniformly read <code>exception.statusCode()</code> / <code>exception.responseBody()</code>.</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="53-currently-unimplemented-capabilities">5.3, Currently unimplemented capabilities<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#53-currently-unimplemented-capabilities" class="hash-link" aria-label="Direct link to 5.3, Currently unimplemented capabilities" title="Direct link to 5.3, Currently unimplemented capabilities" translate="no">​</a></h2>
<p>Implemented: Streaming/SSE, strongly-typed <code>tool_use</code> responses (including streaming <code>input_json_delta</code> aggregation), strongly-typed <code>tool_result</code> requests, <code>tools/tool_choice</code> request mapping, <code>UserMessage</code> multiple <code>Content</code> → <code>content[]</code>.</p>
<p>Not yet fully supported: thinking / redacted thinking, image / document content blocks, Prompt Caching strongly-typed configuration and usage, Structured Output, server tools, extended usage fields (cache token, etc.), writing <code>stop_sequence</code> into metadata, provider refusal / stop details normalization.</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="54-class-structure-and-evolution">5.4, Class structure and evolution<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#54-class-structure-and-evolution" class="hash-link" aria-label="Direct link to 5.4, Class structure and evolution" title="Direct link to 5.4, Class structure and evolution" translate="no">​</a></h2>
<p>Blocking and Streaming share the same set of wire mapping:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">AnthropicProtocol（package-private）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    ├── collectSystemMessages / serializeMessages</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    ├── serializeTools / toolChoice</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    ├── extractText / extractToolUses</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ├── AnthropicChatModel          -&gt; blocking Messages API</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        └── AnthropicStreamingChatModel -&gt; Messages API SSE</span><br></div></code></pre></div></div>
<p>When introducing Thinking / Multimodal later, it is recommended to extend a unified Content hierarchy in Core (<code>TextContent</code> already exists): <code>ImageContent</code> / <code>AudioContent</code> / <code>VideoContent</code> / <code>PdfFileContent</code>, so that the Anthropic block protocol and OpenAI multimodal converge onto the same upper-layer model.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="vi-summary">VI. Summary<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#vi-summary" class="hash-link" aria-label="Direct link to VI. Summary" title="Direct link to VI. Summary" translate="no">​</a></h2>
<ol>
<li class=""><strong>Two major differences</strong>: The top-level <code>system</code> field and the <code>content</code> block array — both are absorbed by the Provider layer, leaving Core unchanged.</li>
<li class=""><strong>Tool calls</strong>: <code>tool_use</code> (<code>input</code> object) → <code>tool_result</code> (written back with the <code>user</code> role), a strongly-typed bidirectional mapping.</li>
<li class=""><strong>Streaming</strong>: <code>AnthropicStreamState</code> parses the SSE with event names itself, and <code>input_json_delta</code> is aggregated by block <code>index</code>.</li>
<li class=""><strong>Required constraint</strong>: <code>max_tokens</code> defaults to 1024, ensuring it can be called directly.</li>
<li class=""><strong>Risk note</strong>: Newer Claude models tighten <code>temperature</code> / <code>top_p</code>; leaving them unset by default is safer.</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DAnthropic%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<p>[1]. <a href="https://platform.claude.com/docs/en/api/messages/create" target="_blank" rel="noopener noreferrer" class="">Anthropic Messages API (English)</a></p>
<p>[2]. <a href="https://platform.claude.com/docs/zh-CN/api/messages/create" target="_blank" rel="noopener noreferrer" class="">Anthropic Messages API (Chinese)</a></p>
<p>[3]. Related internal documents: <a class="" href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3">A Quick Understanding of the Anthropic Low-Level Protocol</a>, [ChatModel Core Protocol Layer Design](/blog/01、model模型协议层/chatmodel/统一ChatModel协议层01、ChatModel 核心协议层设计：统一 API 契约封装与多协议扩展实现)</p>
<br>
<p>Compiled by: changlu Created: 2026.10.5 Updated: 2026.10.5</p>]]></content:encoded>
            <category>AgentForge</category>
            <category>Model Protocol Layer</category>
            <category>Anthropic</category>
        </item>
        <item>
            <title><![CDATA[Anthropic Protocol 01, A Quick Understanding of the Anthropic Low-Level Protocol]]></title>
            <link>https://agentforge-tech.github.io/AgentForge/en/blog/01、model模型协议层/chatmodel/anthropic/Anthropic协议01、Anthropic底层协议快速理解</link>
            <guid>https://agentforge-tech.github.io/AgentForge/en/blog/01、model模型协议层/chatmodel/anthropic/Anthropic协议01、Anthropic底层协议快速理解</guid>
            <pubDate>Mon, 05 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Updated: 2026-10-05]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>Updated: 2026-10-05<br>
<!-- -->Applicable scenario: Understanding the Anthropic Messages API that <code>agentforge-model-anthropic</code> connects to<br>
<!-- -->Maintainer: changlu</p>
</blockquote>
<!-- -->
<p>One-sentence conclusion: Anthropic connects to the <strong>Messages API</strong> (<code>POST {baseUrl}/v1/messages</code>), and it has two major differences — <strong>system is a top-level field</strong> and <strong>content is a block array</strong>. This article only covers "what the protocol itself looks like"; for AgentForge's mapping, see "Anthropic Protocol 02, Core Practices for AgentForge's Anthropic Integration".</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="i-background-and-problem-introduction">I. Background and Problem Introduction<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#i-background-and-problem-introduction" class="hash-link" aria-label="Direct link to I. Background and Problem Introduction" title="Direct link to I. Background and Problem Introduction" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="11-scenario-driven-claudes-tool_use-and-content-block">1.1, Scenario-Driven: Claude's tool_use and content block<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#11-scenario-driven-claudes-tool_use-and-content-block" class="hash-link" aria-label="Direct link to 1.1, Scenario-Driven: Claude's tool_use and content block" title="Direct link to 1.1, Scenario-Driven: Claude's tool_use and content block" translate="no">​</a></h2>
<blockquote>
<p>When integrating Claude for AgentForge, we found that Anthropic and OpenAI differ considerably at the wire level: different auth headers, a different location for the system prompt, content being a block array rather than a string, and tool calls expressed via <code>tool_use</code> / <code>tool_result</code>.</p>
</blockquote>
<p>Without first understanding these wire details, we cannot correctly normalize responses into AgentForge's unified messages.</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="12-problem-guidance-what-does-a-single-claude-call-look-like">1.2, Problem Guidance: What does a single Claude call look like?<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#12-problem-guidance-what-does-a-single-claude-call-look-like" class="hash-link" aria-label="Direct link to 1.2, Problem Guidance: What does a single Claude call look like?" title="Direct link to 1.2, Problem Guidance: What does a single Claude call look like?" translate="no">​</a></h2>
<blockquote>
<p><strong>Question</strong>: For a single Messages API call, what are the request headers, <code>system</code>, <code>messages</code>, <code>content</code> blocks, and streaming SSE events? How are tool calls expressed in both directions?</p>
</blockquote>
<p>This article explains it in the order of "request → response → streaming → tool calls".</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="13-protocol-positioning">1.3, Protocol Positioning<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#13-protocol-positioning" class="hash-link" aria-label="Direct link to 1.3, Protocol Positioning" title="Direct link to 1.3, Protocol Positioning" translate="no">​</a></h2>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">POST {baseUrl}/v1/messages</span><br></div></code></pre></div></div>
<p>Default:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">baseUrl = https://api.anthropic.com</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">anthropic-version = 2023-06-01</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">→ actual address https://api.anthropic.com/v1/messages</span><br></div></code></pre></div></div>
<p>The Messages API is a <strong>stateless</strong> message protocol: the client submits the current conversation history, and the model generates the next assistant message.</p>
<p>Official references:</p>
<ul>
<li class=""><a href="https://platform.claude.com/docs/en/api/messages/create" target="_blank" rel="noopener noreferrer" class="">https://platform.claude.com/docs/en/api/messages/create</a></li>
<li class=""><a href="https://platform.claude.com/docs/zh-CN/api/messages/create" target="_blank" rel="noopener noreferrer" class="">https://platform.claude.com/docs/zh-CN/api/messages/create</a></li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ii-request-protocol">II. Request Protocol<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#ii-request-protocol" class="hash-link" aria-label="Direct link to II. Request Protocol" title="Direct link to II. Request Protocol" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-headers">2.1, Headers<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#21-headers" class="hash-link" aria-label="Direct link to 2.1, Headers" title="Direct link to 2.1, Headers" translate="no">​</a></h2>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Content-Type: application/json</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Accept: application/json</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">x-api-key: ${ANTHROPIC_API_KEY}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">anthropic-version: 2023-06-01</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: Authentication is not <code>Authorization: Bearer</code> but <code>x-api-key</code>; moreover, the API version Header must be sent explicitly.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-system-is-a-top-level-field-key-difference">2.2, system is a top-level field (key difference)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#22-system-is-a-top-level-field-key-difference" class="hash-link" aria-label="Direct link to 2.2, system is a top-level field (key difference)" title="Direct link to 2.2, system is a top-level field (key difference)" translate="no">​</a></h2>
<p>Anthropic's system prompt does <strong>not use a <code>system</code> role message</strong>, but rather a top-level <code>system</code> field:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"model"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"your-claude-model"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"max_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">1024</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"system"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"You are a concise Java assistant."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"messages"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"user"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Explain volatile."</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Note</strong>: Multiple system prompts need to be concatenated by the client (AgentForge concatenates with a double newline <code>\n\n</code>).</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="23-messages-role-and-content">2.3, messages: role and content<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#23-messages-role-and-content" class="hash-link" aria-label="Direct link to 2.3, messages: role and content" title="Direct link to 2.3, messages: role and content" translate="no">​</a></h2>
<table><thead><tr><th>role</th><th>content</th><th>Description</th></tr></thead><tbody><tr><td><code>user</code></td><td>string or block array</td><td>User input; tool results are also carried by <code>user</code></td></tr><tr><td><code>assistant</code></td><td>string or block array</td><td>Model reply; tool calls use the <code>tool_use</code> block</td></tr></tbody></table>
<p>The conversation history is usually alternating <code>user</code> / <code>assistant</code> turns; consecutive messages with the same role may be merged by the server.</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="24-content-block-types">2.4, content block types<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#24-content-block-types" class="hash-link" aria-label="Direct link to 2.4, content block types" title="Direct link to 2.4, content block types" translate="no">​</a></h2>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">text</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">tool_use</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">tool_result</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">thinking / redacted_thinking</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">image / document</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">server tool blocks</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">...</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="25-request-parameters">2.5, Request parameters<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#25-request-parameters" class="hash-link" aria-label="Direct link to 2.5, Request parameters" title="Direct link to 2.5, Request parameters" translate="no">​</a></h2>
<table><thead><tr><th>Field</th><th>Description</th></tr></thead><tbody><tr><td><code>model</code></td><td>Model ID</td></tr><tr><td><code>max_tokens</code></td><td><strong>Required</strong>, maximum generated tokens</td></tr><tr><td><code>temperature</code> / <code>top_p</code></td><td>Sampling parameters (deprecated/restricted for new models, see below)</td></tr><tr><td><code>stop_sequences</code></td><td>Stop sequences</td></tr><tr><td><code>tools[]</code></td><td><code>{"name","description","input_schema"}</code></td></tr><tr><td><code>tool_choice</code></td><td><code>{"type":"auto"}</code> / <code>{"type":"none"}</code> / <code>{"type":"any"}</code> / <code>{"type":"tool","name":X}</code></td></tr></tbody></table>
<blockquote>
<p><strong>Note</strong>: Anthropic has officially marked <code>temperature</code> and <code>top_p</code> as deprecated/restricted parameters for new models; new models may only accept compatible values, otherwise returning 400. Whether to send them should be decided based on the target model's capabilities.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="iii-response-protocol">III. Response Protocol<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#iii-response-protocol" class="hash-link" aria-label="Direct link to III. Response Protocol" title="Direct link to III. Response Protocol" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-message-structure">3.1, message structure<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#31-message-structure" class="hash-link" aria-label="Direct link to 3.1, message structure" title="Direct link to 3.1, message structure" translate="no">​</a></h2>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"msg_xxx"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"message"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"model"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"your-claude-model"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"text"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"text"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"volatile provides visibility guarantees..."</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"stop_reason"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"end_turn"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"stop_sequence"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token null keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"usage"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"input_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">24</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"output_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">40</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: <code>content</code> is a <strong>block array</strong>, not a single string; a response may contain multiple text blocks and possibly a <code>tool_use</code> block.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-stop_reason">3.2, stop_reason<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#32-stop_reason" class="hash-link" aria-label="Direct link to 3.2, stop_reason" title="Direct link to 3.2, stop_reason" translate="no">​</a></h2>
<table><thead><tr><th>Value</th><th>Meaning</th></tr></thead><tbody><tr><td><code>end_turn</code></td><td>Natural end</td></tr><tr><td><code>stop_sequence</code></td><td>Hit a custom stop sequence</td></tr><tr><td><code>max_tokens</code></td><td>Reached max_tokens</td></tr><tr><td><code>tool_use</code></td><td>Needs to execute a tool</td></tr></tbody></table>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-usage">3.3, usage<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#33-usage" class="hash-link" aria-label="Direct link to 3.3, usage" title="Direct link to 3.3, usage" translate="no">​</a></h2>
<p><code>input_tokens</code> / <code>output_tokens</code>; the official API may also return extended fields such as cache tokens and server tool usage.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="iv-streaming-protocol-sse">IV. Streaming Protocol (SSE)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#iv-streaming-protocol-sse" class="hash-link" aria-label="Direct link to IV. Streaming Protocol (SSE)" title="Direct link to IV. Streaming Protocol (SSE)" translate="no">​</a></h2>
<p>Add to the request:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"stream"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The response is <strong>SSE with event names</strong>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: message_start</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"message_start","message":{...}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: content_block_delta</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hel"}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: message_stop</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"message_stop"}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="41-event-overview">4.1, Event overview<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#41-event-overview" class="hash-link" aria-label="Direct link to 4.1, Event overview" title="Direct link to 4.1, Event overview" translate="no">​</a></h2>
<table><thead><tr><th>Event</th><th>Meaning</th></tr></thead><tbody><tr><td><code>message_start</code></td><td>Start, carrying message metadata and <code>usage.input_tokens</code></td></tr><tr><td><code>content_block_start</code></td><td>Start a content block (including the <code>tool_use</code>'s <code>id</code> / <code>name</code> / initial <code>input</code>)</td></tr><tr><td><code>content_block_delta</code></td><td>Increment: <code>text_delta</code> or <code>input_json_delta</code></td></tr><tr><td><code>content_block_stop</code></td><td>Block end</td></tr><tr><td><code>message_delta</code></td><td>Carries <code>delta.stop_reason</code> and <code>usage.output_tokens</code></td></tr><tr><td><code>message_stop</code></td><td>Message end</td></tr><tr><td><code>error</code></td><td>Error event</td></tr></tbody></table>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="42-streaming-aggregation-of-tool-arguments">4.2, Streaming aggregation of tool arguments<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#42-streaming-aggregation-of-tool-arguments" class="hash-link" aria-label="Direct link to 4.2, Streaming aggregation of tool arguments" title="Direct link to 4.2, Streaming aggregation of tool arguments" translate="no">​</a></h2>
<p><code>tool_use</code> arguments are delivered incrementally as <code>input_json_delta</code>, attributed by the content block <code>index</code>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">content_block_start (index=1, type=tool_use, id=toolu_1, name=get_weather, input={})</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">content_block_delta (index=1, input_json_delta: "{\"city\":")</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">content_block_delta (index=1, input_json_delta: "\"hangzhou\"}")</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼ accumulate by content block index</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">ToolRequest(id=toolu_1, name=get_weather, arguments={"city":"hangzhou"})</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Note</strong>: Text blocks and tool blocks can interleave within the same stream; <code>partial_json</code> concatenates the raw JSON text in arrival order, without reformatting.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="v-tool-use-wire-details">V. Tool Use wire details<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#v-tool-use-wire-details" class="hash-link" aria-label="Direct link to V. Tool Use wire details" title="Direct link to V. Tool Use wire details" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="51-assistant-initiates-a-call-tool_use-block">5.1, assistant initiates a call (tool_use block)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#51-assistant-initiates-a-call-tool_use-block" class="hash-link" aria-label="Direct link to 5.1, assistant initiates a call (tool_use block)" title="Direct link to 5.1, assistant initiates a call (tool_use block)" translate="no">​</a></h2>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"text"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"text"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"I will check."</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tool_use"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"toolu_1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"input"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"city"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"hangzhou"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: Unlike OpenAI's <code>arguments</code> string, Anthropic's <code>input</code> is an <strong>object</strong>.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="52-tool-result-write-back-tool_result-block-roleuser">5.2, Tool result write-back (tool_result block, role=user)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#52-tool-result-write-back-tool_result-block-roleuser" class="hash-link" aria-label="Direct link to 5.2, Tool result write-back (tool_result block, role=user)" title="Direct link to 5.2, Tool result write-back (tool_result block, role=user)" translate="no">​</a></h2>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"user"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tool_result"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"tool_use_id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"toolu_1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"{\"temperature\":22}"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p><code>tool_use_id</code> corresponds to the <code>tool_use.id</code> at initiation.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="vi-complete-call-walkthrough-user-prompt--tool-use-curl">VI. Complete Call Walkthrough: user prompt + tool use (curl)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#vi-complete-call-walkthrough-user-prompt--tool-use-curl" class="hash-link" aria-label="Direct link to VI. Complete Call Walkthrough: user prompt + tool use (curl)" title="Direct link to VI. Complete Call Walkthrough: user prompt + tool use (curl)" translate="no">​</a></h2>
<p>Scenario: The user asks <strong>"What's the weather like in Hangzhou today?"</strong> and declares a <code>get_weather</code> tool. Below, from constructing the curl to the return, both non-streaming / streaming are demonstrated (including the second round of tool result write-back).</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="61-declaring-tools">6.1, Declaring tools<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#61-declaring-tools" class="hash-link" aria-label="Direct link to 6.1, Declaring tools" title="Direct link to 6.1, Declaring tools" translate="no">​</a></h2>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token property">"tools"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"description"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"查询指定城市的当前天气"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"input_schema"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"object"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"properties"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"city"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"string"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"description"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"城市名"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"required"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"city"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Note</strong>: Anthropic tools use <code>input_schema</code> (not OpenAI's <code>parameters</code>).</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="62-non-streaming--round-1-the-model-decides-to-call-a-tool">6.2, Non-streaming · Round 1: The model decides to call a tool<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#62-non-streaming--round-1-the-model-decides-to-call-a-tool" class="hash-link" aria-label="Direct link to 6.2, Non-streaming · Round 1: The model decides to call a tool" title="Direct link to 6.2, Non-streaming · Round 1: The model decides to call a tool" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> https://api.anthropic.com/v1/messages </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"x-api-key: </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$ANTHROPIC_API_KEY</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"anthropic-version: 2023-06-01"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Content-Type: application/json"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-d</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'{</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "model": "claude-3-5-sonnet-latest",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "max_tokens": 1024,</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "system": "你是一个简洁的助手。",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "messages": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      { "role": "user", "content": "杭州今天天气怎么样？" }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "tools": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      { "name": "get_weather",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        "description": "查询指定城市的当前天气",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        "input_schema": { "type": "object",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "properties": { "city": { "type": "string", "description": "城市名" } },</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "required": ["city"] } }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "tool_choice": { "type": "auto" }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">  }'</span><br></div></code></pre></div></div>
<p><strong>Return (note <code>stop_reason=tool_use</code>, <code>content</code> contains a <code>tool_use</code> block):</strong></p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"msg_abc"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"message"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"model"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"claude-3-5-sonnet-latest"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"text"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"text"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"我来查询一下杭州的天气。"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tool_use"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"toolu_1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"input"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"city"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"杭州"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"stop_reason"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tool_use"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"stop_sequence"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token null keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"usage"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"input_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">88</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"output_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">35</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: <code>tool_use.input</code> is an <strong>object</strong> <code>{"city":"杭州"}</code> (different from OpenAI's <code>arguments</code> string).</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="63-non-streaming--round-2-write-back-the-tool-result-and-get-the-final-answer">6.3, Non-streaming · Round 2: Write back the tool result and get the final answer<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#63-non-streaming--round-2-write-back-the-tool-result-and-get-the-final-answer" class="hash-link" aria-label="Direct link to 6.3, Non-streaming · Round 2: Write back the tool result and get the final answer" title="Direct link to 6.3, Non-streaming · Round 2: Write back the tool result and get the final answer" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> https://api.anthropic.com/v1/messages </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"x-api-key: </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$ANTHROPIC_API_KEY</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"anthropic-version: 2023-06-01"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Content-Type: application/json"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-d</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'{</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "model": "claude-3-5-sonnet-latest",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "max_tokens": 1024,</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "system": "你是一个简洁的助手。",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "messages": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      { "role": "user", "content": "杭州今天天气怎么样？" },</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      { "role": "assistant", "content": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          { "type": "text", "text": "我来查询一下杭州的天气。" },</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          { "type": "tool_use", "id": "toolu_1", "name": "get_weather",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "input": { "city": "杭州" } } ] },</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      { "role": "user", "content": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          { "type": "tool_result", "tool_use_id": "toolu_1",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "content": "{\"temperature\":26,\"text\":\"晴\"}" } ] }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "tools": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      { "name": "get_weather", "description": "查询指定城市的当前天气",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        "input_schema": { "type": "object",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "properties": { "city": { "type": "string" } }, "required": ["city"] } }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">  }'</span><br></div></code></pre></div></div>
<p><strong>Return (<code>stop_reason=end_turn</code>):</strong></p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"msg_def"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"message"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"text"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"text"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"杭州今天 26℃，天气晴。"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"stop_reason"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"end_turn"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"usage"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"input_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">140</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"output_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">16</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="64-streaming--round-1-input_json_delta-increments-streamtrue">6.4, Streaming · Round 1: <code>input_json_delta</code> increments (<code>stream:true</code>)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#64-streaming--round-1-input_json_delta-increments-streamtrue" class="hash-link" aria-label="Direct link to 64-streaming--round-1-input_json_delta-increments-streamtrue" title="Direct link to 64-streaming--round-1-input_json_delta-increments-streamtrue" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-N</span><span class="token plain"> https://api.anthropic.com/v1/messages </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"x-api-key: </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$ANTHROPIC_API_KEY</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"anthropic-version: 2023-06-01"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Content-Type: application/json"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-d</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'{</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "model": "claude-3-5-sonnet-latest",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "max_tokens": 1024,</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "stream": true,</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "messages": [ { "role": "user", "content": "杭州今天天气怎么样？" } ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "tools": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      { "name": "get_weather", "description": "查询指定城市的当前天气",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        "input_schema": { "type": "object",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "properties": { "city": { "type": "string" } }, "required": ["city"] } }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "tool_choice": { "type": "auto" }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">  }'</span><br></div></code></pre></div></div>
<p><strong>SSE (in arrival order, note <code>event:</code> + <code>data:</code>):</strong></p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: message_start</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"message_start","message":{"id":"msg_abc","role":"assistant","content":[],"usage":{"input_tokens":88,"output_tokens":1}}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: content_block_start</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: content_block_delta</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"我来查询一下杭州的天气。"}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: content_block_stop</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"content_block_stop","index":0}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: content_block_start</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"content_block_start","index":1,"content_block":{"type":"tool_use","id":"toolu_1","name":"get_weather","input":{}}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: content_block_delta</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"{\"city\":"}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: content_block_delta</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"\"杭州\"}"}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: content_block_stop</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"content_block_stop","index":1}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: message_delta</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"message_delta","delta":{"stop_reason":"tool_use"},"usage":{"output_tokens":35}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: message_stop</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"message_stop"}</span><br></div></code></pre></div></div>
<p><strong>Aggregation</strong> (attributed by content block <code>index</code>; <code>partial_json</code> concatenated):</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">index=1 (tool_use) → id=toolu_1, name=get_weather, input={"city":"杭州"}</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="65-streaming--round-2-generate-the-final-text-after-write-back">6.5, Streaming · Round 2: Generate the final text after write-back<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#65-streaming--round-2-generate-the-final-text-after-write-back" class="hash-link" aria-label="Direct link to 6.5, Streaming · Round 2: Generate the final text after write-back" title="Direct link to 6.5, Streaming · Round 2: Generate the final text after write-back" translate="no">​</a></h2>
<p>The request body is the same as 6.3, with <code>"stream": true</code> appended.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: message_start</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"message_start","message":{"id":"msg_def","role":"assistant","content":[],"usage":{"input_tokens":140,"output_tokens":1}}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: content_block_start</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: content_block_delta</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"杭州今天 26℃"}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: content_block_delta</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"，天气晴。"}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: content_block_stop</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"content_block_stop","index":0}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: message_delta</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"output_tokens":16}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: message_stop</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"message_stop"}</span><br></div></code></pre></div></div>
<p><strong>Aggregation</strong>: <code>text_delta</code> concatenated in order → <code>"杭州今天 26℃，天气晴。"</code>.</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="66-walkthrough-key-points-summary">6.6, Walkthrough key points summary<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#66-walkthrough-key-points-summary" class="hash-link" aria-label="Direct link to 6.6, Walkthrough key points summary" title="Direct link to 6.6, Walkthrough key points summary" translate="no">​</a></h2>
<table><thead><tr><th>Observation point</th><th>Non-streaming</th><th>Streaming</th></tr></thead><tbody><tr><td>Tool arguments</td><td><code>content[].tool_use.input</code> (object)</td><td><code>content_block_delta.input_json_delta.partial_json</code> fragments, aggregated by block <code>index</code></td></tr><tr><td>Whether a tool is called</td><td><code>stop_reason == "tool_use"</code></td><td><code>message_delta.delta.stop_reason == "tool_use"</code></td></tr><tr><td>Text</td><td><code>content[].text</code></td><td><code>content_block_delta.text_delta.text</code> called back segment by segment</td></tr><tr><td>usage</td><td>Response body <code>usage</code></td><td><code>message_start</code> (input) + <code>message_delta</code> (output)</td></tr><tr><td>End</td><td>—</td><td><code>event: message_stop</code></td></tr></tbody></table>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="67-real-test-verification">6.7, Real test verification<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#67-real-test-verification" class="hash-link" aria-label="Direct link to 6.7, Real test verification" title="Direct link to 6.7, Real test verification" translate="no">​</a></h2>
<p>You can directly run the script in the repository (requires setting the API Key):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token builtin class-name" style="color:rgb(189, 147, 249)">export</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">ANTHROPIC_API_KEY</span><span class="token operator">=</span><span class="token plain">sk-ant-</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token plain">.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">bash</span><span class="token plain"> docs/dev/backend/AgentForge核心模块设计原理/01、model模型协议层/chatmodel/anthropic/verify-anthropic-chat.sh</span><br></div></code></pre></div></div>
<p>The script executes in sequence: ① a non-streaming tool call request; ② printing the <code>tool_use</code> block with <code>jq</code>; ③ a streaming request printing SSE. The optional environment variables <code>ANTHROPIC_BASE_URL</code> / <code>ANTHROPIC_MODEL</code> override the defaults.</p>
<blockquote>
<p><strong>Note</strong>: Real calls incur costs; do not write the Key into scripts or commit it to the repository.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="vii-summary">VII. Summary<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#vii-summary" class="hash-link" aria-label="Direct link to VII. Summary" title="Direct link to VII. Summary" translate="no">​</a></h2>
<ol>
<li class=""><strong>Endpoint</strong>: <code>POST {baseUrl}/v1/messages</code>; <code>x-api-key</code> + <code>anthropic-version</code> authentication.</li>
<li class=""><strong>system</strong>: A top-level field, not a role message.</li>
<li class=""><strong>content</strong>: A block array (<code>text</code> / <code>tool_use</code> / <code>tool_result</code> / …).</li>
<li class=""><strong>Request</strong>: <code>model</code> + required <code>max_tokens</code> + sampling/tool parameters.</li>
<li class=""><strong>Response</strong>: <code>content[]</code> + <code>stop_reason</code> + <code>usage</code>.</li>
<li class=""><strong>Streaming</strong>: SSE with event names; tool arguments aggregate <code>input_json_delta</code> by block <code>index</code>.</li>
<li class=""><strong>Tools</strong>: <code>tool_use</code> (<code>input</code> is an object) → <code>tool_result</code> (written back with the <code>user</code> role).</li>
</ol>
<blockquote>
<p>For how AgentForge maps these to unified types, see "Anthropic Protocol 02, Core Practices for AgentForge's Anthropic Integration".</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<p>[1]. <a href="https://platform.claude.com/docs/en/api/messages/create" target="_blank" rel="noopener noreferrer" class="">Anthropic Messages API (English)</a></p>
<p>[2]. <a href="https://platform.claude.com/docs/zh-CN/api/messages/create" target="_blank" rel="noopener noreferrer" class="">Anthropic Messages API (Chinese)</a></p>
<p>[3]. <a href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events" target="_blank" rel="noopener noreferrer" class="">Server-Sent Events (MDN)</a></p>
<p>[4]. Related internal document: [Core Practices for AgentForge Anthropic Integration](/blog/01、model模型协议层/chatmodel/anthropic/Anthropic协议02、AgentForge 适配Anthropic接入核心实践)</p>
<br>
<p>Compiled by: changlu Created: 2026.10.5 Updated: 2026.10.5</p>]]></content:encoded>
            <category>AgentForge</category>
            <category>Model Protocol Layer</category>
            <category>Anthropic</category>
        </item>
        <item>
            <title><![CDATA[OpenAI Protocol 02, AgentForge OpenAI Integration Core Practices]]></title>
            <link>https://agentforge-tech.github.io/AgentForge/en/blog/01、model模型协议层/chatmodel/openai/OpenAI协议02、AgentForge 适配OpenAI接入核心实践</link>
            <guid>https://agentforge-tech.github.io/AgentForge/en/blog/01、model模型协议层/chatmodel/openai/OpenAI协议02、AgentForge 适配OpenAI接入核心实践</guid>
            <pubDate>Sun, 04 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Updated: 2026-10-05]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>Updated: 2026-10-05<br>
<!-- -->Applicable module: <code>agentforge-model-openai</code><br>
<!-- -->Current implementation: <code>OpenAiChatModel</code> (Blocking), <code>OpenAiStreamingChatModel</code> (SSE)<br>
<!-- -->Wire API: OpenAI Chat Completions<br>
<!-- -->Maintainer: changlu</p>
</blockquote>
<!-- -->
<p>One-sentence conclusion: The OpenAI Provider does a bidirectional mapping between AgentForge's unified <code>ChatRequest</code> / <code>ChatResponse</code> and the <strong>Chat Completions</strong> wire format; for the protocol itself see "OpenAI Protocol 01, A Quick Understanding of the OpenAI Low-Level Protocol", and this article covers AgentForge's implementation and boundaries.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-background-and-problem-introduction">1. Background and Problem Introduction<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#1-background-and-problem-introduction" class="hash-link" aria-label="Direct link to 1. Background and Problem Introduction" title="Direct link to 1. Background and Problem Introduction" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="11-scenario-driven-one-core-adapting-to-multiple-models">1.1 Scenario-Driven: One Core, Adapting to Multiple Models<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#11-scenario-driven-one-core-adapting-to-multiple-models" class="hash-link" aria-label="Direct link to 1.1 Scenario-Driven: One Core, Adapting to Multiple Models" title="Direct link to 1.1 Scenario-Driven: One Core, Adapting to Multiple Models" translate="no">​</a></h2>
<blockquote>
<p>AgentForge's Agent Runtime depends only on the <code>ChatModel</code> / <code>StreamingChatModel</code> interfaces. We want "switching models" to mean switching just one Provider dependency, rather than changing Agent code.</p>
</blockquote>
<p>So the OpenAI Provider's responsibility is limited to being a <strong>protocol translation layer</strong>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Agent Runtime</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     │  ChatRequest / ChatResponse (Provider-agnostic)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">OpenAiChatModel / OpenAiStreamingChatModel</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     │  Chat Completions wire</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">OpenAI / OpenAI-compatible service</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="12-problem-guidance-how-do-unified-types-land-on-the-openai-wire">1.2 Problem Guidance: How Do Unified Types Land on the OpenAI wire?<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#12-problem-guidance-how-do-unified-types-land-on-the-openai-wire" class="hash-link" aria-label="Direct link to 1.2 Problem Guidance: How Do Unified Types Land on the OpenAI wire?" title="Direct link to 1.2 Problem Guidance: How Do Unified Types Land on the OpenAI wire?" translate="no">​</a></h2>
<blockquote>
<p><strong>Question</strong>: How do AgentForge's <code>ChatMessage</code>, <code>ChatRequestParameters</code>, and <code>ChatResponse</code> land on OpenAI's <code>messages</code> / request fields / response fields respectively? How are tool calls mapped bidirectionally? How is streaming aggregated?</p>
</blockquote>
<p>This article provides the mapping tables and implementation locations item by item.</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="13-implementation-boundaries">1.3 Implementation Boundaries<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#13-implementation-boundaries" class="hash-link" aria-label="Direct link to 1.3 Implementation Boundaries" title="Direct link to 1.3 Implementation Boundaries" translate="no">​</a></h2>
<ul>
<li class="">Wire API: <code>POST {baseUrl}/chat/completions</code> (default <code>https://api.openai.com/v1</code>);</li>
<li class="">release_1.x uses Chat Completions as the first version of the unified protocol, and the Responses API adopts a "coexisting Adapter" strategy (see Chapter 6).</li>
</ul>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="14-builder-parameters">1.4 Builder Parameters<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#14-builder-parameters" class="hash-link" aria-label="Direct link to 1.4 Builder Parameters" title="Direct link to 1.4 Builder Parameters" translate="no">​</a></h2>
<table><thead><tr><th>Builder parameter</th><th>Default value</th><th>Description</th></tr></thead><tbody><tr><td><code>baseUrl</code></td><td><code>https://api.openai.com/v1</code></td><td>Also supports OpenAI-compatible services</td></tr><tr><td><code>apiKey</code></td><td><code>null</code></td><td>Sends <code>Authorization: Bearer ...</code> when non-empty</td></tr><tr><td><code>modelName</code></td><td><code>null</code></td><td>Must have a value before the request</td></tr><tr><td><code>temperature</code></td><td><code>null</code></td><td>Only sent when non-empty</td></tr><tr><td><code>maxTokens</code></td><td><code>null</code></td><td>Maps to <code>max_tokens</code></td></tr><tr><td><code>topP</code></td><td><code>null</code></td><td>Maps to <code>top_p</code></td></tr><tr><td><code>stopSequences</code></td><td><code>null</code></td><td>Maps to <code>stop</code></td></tr><tr><td><code>customParameter</code></td><td>empty</td><td>Passes through Provider-specific top-level fields</td></tr><tr><td><code>customHeader</code></td><td>empty</td><td>Adds custom HTTP Headers</td></tr><tr><td><code>httpTransport</code></td><td><code>JdkHttpTransport</code></td><td>HTTP SPI</td></tr><tr><td><code>connectTimeoutMillis</code></td><td><code>10000</code></td><td>Connection timeout</td></tr><tr><td><code>readTimeoutMillis</code></td><td><code>60000</code></td><td>Read timeout</td></tr></tbody></table>
<blockquote>
<p><strong>Key point</strong>: <code>OpenAiStreamingChatModel</code> reuses the same set of configuration and forcibly writes <code>stream=true</code> and <code>stream_options.include_usage=true</code>.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-core-concepts">2. Core Concepts<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#2-core-concepts" class="hash-link" aria-label="Direct link to 2. Core Concepts" title="Direct link to 2. Core Concepts" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-unified-input">2.1 Unified Input<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#21-unified-input" class="hash-link" aria-label="Direct link to 2.1 Unified Input" title="Direct link to 2.1 Unified Input" translate="no">​</a></h2>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">ChatRequest</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">List</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ChatMessage</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> messages</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">ChatRequestParameters</span><span class="token plain"> parameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ChatRequestParameters</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">modelName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">Double</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">temperature</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">Integer</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">maxTokens</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">Double</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">topP</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">List</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">stopSequences</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">Object</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">customParameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-unified-output">2.2 Unified Output<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#22-unified-output" class="hash-link" aria-label="Direct link to 2.2 Unified Output" title="Direct link to 2.2 Unified Output" translate="no">​</a></h2>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">ChatResponse</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">AiMessage</span><span class="token plain"> aiMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">          </span><span class="token comment" style="color:rgb(98, 114, 164)">// text + toolExecutionRequests</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">TokenUsage</span><span class="token plain"> tokenUsage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">FinishReason</span><span class="token plain"> finishReason</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">Object</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: The Provider's responsibility is to land the wire fields losslessly onto these two unified objects.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-implementation-approach-and-mapping">3. Implementation Approach and Mapping<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#3-implementation-approach-and-mapping" class="hash-link" aria-label="Direct link to 3. Implementation Approach and Mapping" title="Direct link to 3. Implementation Approach and Mapping" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-http-headers">3.1 HTTP Headers<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#31-http-headers" class="hash-link" aria-label="Direct link to 3.1 HTTP Headers" title="Direct link to 3.1 HTTP Headers" translate="no">​</a></h2>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Content-Type: application/json</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Accept: application/json</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Authorization: Bearer ${apiKey}   # when apiKey is non-empty</span><br></div></code></pre></div></div>
<p>Afterwards <code>customHeaders</code> is appended; enterprise internal gateways can use <code>customHeader(...)</code> to add tenant, routing, trace, etc.</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-message-mapping">3.2 Message Mapping<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#32-message-mapping" class="hash-link" aria-label="Direct link to 3.2 Message Mapping" title="Direct link to 3.2 Message Mapping" translate="no">​</a></h2>
<table><thead><tr><th>AgentForge</th><th>OpenAI role</th><th>content / key field</th></tr></thead><tbody><tr><td><code>SystemMessage</code></td><td><code>system</code></td><td><code>message.text()</code></td></tr><tr><td><code>UserMessage</code> (single text)</td><td><code>user</code></td><td><code>message.text()</code></td></tr><tr><td><code>UserMessage</code> (multiple <code>Content</code>)</td><td><code>user</code></td><td><code>content[]</code>, each <code>TextContent</code> → <code>{"type":"text","text":...}</code></td></tr><tr><td><code>AiMessage</code> (plain text)</td><td><code>assistant</code></td><td><code>message.text()</code></td></tr><tr><td><code>AiMessage</code> (with tool calls)</td><td><code>assistant</code></td><td><code>content</code> (may be <code>null</code>) + <code>tool_calls[]</code></td></tr><tr><td><code>ToolExecutionResultMessage</code></td><td><code>tool</code></td><td><code>tool_call_id = message.id()</code>, <code>content = message.text()</code></td></tr></tbody></table>
<p>The wire form of tool calls + result fill-back:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"messages"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token null keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"tool_calls"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"call_1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"function"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token property">"function"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"getWeather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"arguments"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"{\"city\":\"hangzhou\"}"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tool"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"tool_call_id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"call_1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"{\"temperature\":22}"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Note</strong>: <code>CustomMessage</code> currently has no wire mapping implemented; encountering one throws <code>IllegalArgumentException</code>.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-parameter-mapping">3.3 Parameter Mapping<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#33-parameter-mapping" class="hash-link" aria-label="Direct link to 3.3 Parameter Mapping" title="Direct link to 3.3 Parameter Mapping" translate="no">​</a></h2>
<table><thead><tr><th>AgentForge parameter</th><th>Chat Completions field</th><th>Current behavior</th></tr></thead><tbody><tr><td><code>modelName</code></td><td><code>model</code></td><td>Required; fails locally if empty</td></tr><tr><td><code>temperature</code></td><td><code>temperature</code></td><td>Only sent when non-empty</td></tr><tr><td><code>maxTokens</code></td><td><code>max_tokens</code></td><td>Only sent when non-empty</td></tr><tr><td><code>topP</code></td><td><code>top_p</code></td><td>Only sent when non-empty</td></tr><tr><td><code>stopSequences</code></td><td><code>stop</code></td><td>Only sent when non-empty</td></tr><tr><td><code>tools</code></td><td><code>tools[]</code></td><td><code>{"type":"function","function":{name,description,parameters,strict}}</code></td></tr><tr><td><code>toolChoice</code></td><td><code>tool_choice</code></td><td><code>AUTO→"auto"</code>, <code>NONE→"none"</code>, <code>REQUIRED→"required"</code>, <code>SPECIFIC→{"type":"function","function":{"name":X}}</code></td></tr><tr><td><code>customParameters</code></td><td>Written verbatim at the top level</td><td>Generic fields are written then override same-named custom fields</td></tr></tbody></table>
<p>Construction order (standard fields have final priority):</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">1. payload.putAll(customParameters)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">2. write model / messages</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">3. write temperature / max_tokens / top_p / stop</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">4. write tools / tool_choice</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: Even if <code>customParameters</code> writes another <code>model</code>, it will ultimately be overridden by <code>modelName</code>.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="34-response-mapping">3.4 Response Mapping<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#34-response-mapping" class="hash-link" aria-label="Direct link to 3.4 Response Mapping" title="Direct link to 3.4 Response Mapping" translate="no">​</a></h2>
<table><thead><tr><th>OpenAI field</th><th>AgentForge field</th></tr></thead><tbody><tr><td><code>choices[0].message.content</code></td><td><code>ChatResponse.aiMessage().text()</code></td></tr><tr><td><code>choices[0].message.tool_calls[]</code></td><td><code>ChatResponse.aiMessage().toolExecutionRequests()</code></td></tr><tr><td><code>usage.prompt_tokens</code></td><td><code>TokenUsage.inputTokens()</code></td></tr><tr><td><code>usage.completion_tokens</code></td><td><code>TokenUsage.outputTokens()</code></td></tr><tr><td><code>usage.total_tokens</code></td><td><code>TokenUsage.totalTokens()</code></td></tr><tr><td><code>choices[0].finish_reason</code></td><td><code>ChatResponse.finishReason()</code></td></tr><tr><td><code>id</code> / <code>model</code> / <code>created</code></td><td><code>metadata["id"] / ["model"] / ["created"]</code></td></tr></tbody></table>
<p><code>tool_calls[]</code> mapping: <code>id → ToolExecutionRequest.id</code>, <code>function.name → name</code>, <code>function.arguments → arguments</code> (preserving the raw JSON string, without reformatting).</p>
<blockquote>
<p><strong>Note</strong>: When <code>tool_calls</code> is non-empty and <code>content</code> is empty, <code>AiMessage.text()</code> returns <code>null</code>; when both exist, both are preserved.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="35-finish_reason-mapping">3.5 finish_reason Mapping<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#35-finish_reason-mapping" class="hash-link" aria-label="Direct link to 3.5 finish_reason Mapping" title="Direct link to 3.5 finish_reason Mapping" translate="no">​</a></h2>
<table><thead><tr><th>OpenAI</th><th>AgentForge <code>FinishReason</code></th></tr></thead><tbody><tr><td><code>stop</code></td><td><code>STOP</code></td></tr><tr><td><code>length</code></td><td><code>LENGTH</code></td></tr><tr><td><code>tool_calls</code></td><td><code>TOOL_EXECUTION</code></td></tr><tr><td><code>function_call</code></td><td><code>TOOL_EXECUTION</code></td></tr><tr><td><code>content_filter</code></td><td><code>CONTENT_FILTER</code></td></tr><tr><td>Other non-empty</td><td><code>OTHER</code></td></tr><tr><td><code>null</code></td><td><code>null</code></td></tr></tbody></table>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="36-abnormal-response-structures">3.6 Abnormal Response Structures<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#36-abnormal-response-structures" class="hash-link" aria-label="Direct link to 3.6 Abnormal Response Structures" title="Direct link to 3.6 Abnormal Response Structures" translate="no">​</a></h2>
<p>The following cases directly throw <code>ModelException</code> (rather than silently returning empty):</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">choices does not exist / is empty / choices[0].message does not exist</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="37-streaming-implementation">3.7 Streaming Implementation<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#37-streaming-implementation" class="hash-link" aria-label="Direct link to 3.7 Streaming Implementation" title="Direct link to 3.7 Streaming Implementation" translate="no">​</a></h2>
<p><strong>Request</strong>: Same Endpoint, forcibly <code>stream=true</code> + <code>stream_options.include_usage=true</code>.</p>
<p><strong>SSE parsing</strong>: Ignore empty lines, <code>:</code> comments, and non-<code>data:</code> lines; each <code>data:</code> chunk reads <code>choices[0].delta.content</code>, <code>delta.tool_calls[]</code>, <code>finish_reason</code>.</p>
<ul>
<li class=""><code>delta.content</code> → immediately <code>handler.onPartialResponse(...)</code>, while aggregating locally with a <code>StringBuilder</code>;</li>
<li class=""><code>delta.tool_calls[]</code> → <strong>accumulate bucketed by <code>index</code></strong> (rules in protocol article 4.1), without calling back half-finished pieces;</li>
<li class="">End → <code>handler.onCompleteResponse(response)</code>.</li>
</ul>
<p><strong>Final response</strong>:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">ChatResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">aiMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">AiMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">fullText</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> toolExecutionRequests</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">finishReason</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">finishReason</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tokenUsage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tokenUsage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: The final usage chunk (<code>choices=[]</code>) is handled by "read usage first, then determine whether choices is empty"; if the stream is interrupted the final usage may be missing, and <code>ChatResponse.tokenUsage()</code> is not guaranteed to exist.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-practical-code">4. Practical Code<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#4-practical-code" class="hash-link" aria-label="Direct link to 4. Practical Code" title="Direct link to 4. Practical Code" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="41-non-streaming">4.1 Non-Streaming<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#41-non-streaming" class="hash-link" aria-label="Direct link to 4.1 Non-Streaming" title="Direct link to 4.1 Non-Streaming" translate="no">​</a></h2>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">OpenAiChatModel</span><span class="token plain"> model </span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">OpenAiChatModel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">baseUrl</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"https://api.openai.com/v1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">apiKey</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getenv</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"OPENAI_API_KEY"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">modelName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"your-model"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">temperature</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">0.2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">maxTokens</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">1024</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ChatRequest</span><span class="token plain"> request </span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">ChatRequest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">SystemMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"You are a concise Java assistant."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">UserMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"What is CAS?"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ChatResponse</span><span class="token plain"> response </span><span class="token operator">=</span><span class="token plain"> model</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">chat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">aiMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">text</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p><strong>Run output (simulated terminal)</strong></p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">$ </span><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-s</span><span class="token plain"> https://api.openai.com/v1/chat/completions </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Authorization: Bearer </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$OPENAI_API_KEY</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-d</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'{"model":"your-model","messages":[{"role":"system","content":"..."},{"role":"user","content":"What is CAS?"}]}'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"choices"</span><span class="token plain">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"index"</span><span class="token plain">:0,</span><span class="token string" style="color:rgb(255, 121, 198)">"message"</span><span class="token plain">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"role"</span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token plain">,</span><span class="token string" style="color:rgb(255, 121, 198)">"content"</span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token string" style="color:rgb(255, 121, 198)">"CAS means Compare-And-Swap."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain">,</span><span class="token string" style="color:rgb(255, 121, 198)">"finish_reason"</span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token string" style="color:rgb(255, 121, 198)">"stop"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain">,</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"usage"</span><span class="token plain">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"prompt_tokens"</span><span class="token plain">:20,</span><span class="token string" style="color:rgb(255, 121, 198)">"completion_tokens"</span><span class="token plain">:10,</span><span class="token string" style="color:rgb(255, 121, 198)">"total_tokens"</span><span class="token plain">:30</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="42-streaming">4.2 Streaming<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#42-streaming" class="hash-link" aria-label="Direct link to 4.2 Streaming" title="Direct link to 4.2 Streaming" translate="no">​</a></h2>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">OpenAiStreamingChatModel</span><span class="token plain"> streaming </span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">OpenAiStreamingChatModel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">baseUrl</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"https://api.openai.com/v1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">apiKey</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getenv</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"OPENAI_API_KEY"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">modelName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"your-model"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">streaming</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">chat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">StreamingChatResponseHandler</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Override</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">onPartialResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">String</span><span class="token plain"> partial</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">print</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">partial</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Override</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">onCompleteResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ChatResponse</span><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Override</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">onError</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Throwable</span><span class="token plain"> error</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> error</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">printStackTrace</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p><strong>Run output (simulated terminal)</strong></p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">CAS means Compare-And-Swap.</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="43-openai-compatible-service">4.3 OpenAI-compatible Service<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#43-openai-compatible-service" class="hash-link" aria-label="Direct link to 4.3 OpenAI-compatible Service" title="Direct link to 4.3 OpenAI-compatible Service" translate="no">​</a></h2>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">OpenAiChatModel</span><span class="token plain"> model </span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">OpenAiChatModel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">baseUrl</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"https://example.com/v1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">apiKey</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"..."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">modelName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"provider-model"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-compatibility-errors-and-boundaries">5. Compatibility, Errors, and Boundaries<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#5-compatibility-errors-and-boundaries" class="hash-link" aria-label="Direct link to 5. Compatibility, Errors, and Boundaries" title="Direct link to 5. Compatibility, Errors, and Boundaries" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="51-http-errors">5.1 HTTP Errors<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#51-http-errors" class="hash-link" aria-label="Direct link to 5.1 HTTP Errors" title="Direct link to 5.1 HTTP Errors" translate="no">​</a></h2>
<p>Non-2xx → <code>new ModelException("OpenAI request failed with HTTP " + statusCode, statusCode, responseBody)</code>; network-layer IOException → <code>ModelException("OpenAI request failed", cause)</code>. For streaming, a non-2xx accumulates the body and returns it via <code>handler.onError(...)</code>.</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="52-protocol-gaps-not-mapped-in-release_1x">5.2 Protocol Gaps (Not Mapped in release_1.x)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#52-protocol-gaps-not-mapped-in-release_1x" class="hash-link" aria-label="Direct link to 5.2 Protocol Gaps (Not Mapped in release_1.x)" title="Direct link to 5.2 Protocol Gaps (Not Mapped in release_1.x)" translate="no">​</a></h2>
<p><code>developer</code> role, image / audio multimodal <code>content</code>, streaming <code>onPartialToolCall</code>, refusal, logprobs, structured outputs, audio, provider reasoning (the <code>AiMessage.thinking()</code> field slot is reserved but not wired), multiple <code>choices</code>.</p>
<blockquote>
<p><code>customParameters</code> can temporarily pass through request fields; but if the returned structure needs to be understood by the framework, the Core / Provider types must still be formally extended.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-summary-and-evolution">6. Summary and Evolution<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#6-summary-and-evolution" class="hash-link" aria-label="Direct link to 6. Summary and Evolution" title="Direct link to 6. Summary and Evolution" translate="no">​</a></h2>
<p>Already delivered: request mapping of the <code>tool</code> role's <code>tool_call_id</code>, <code>tools/tool_choice</code> (including <code>SPECIFIC</code>), non-streaming parsing of <code>tool_calls</code> and streaming aggregation by <code>index</code>, <code>UserMessage</code> multiple <code>Content</code> → <code>content[]</code>.</p>
<p>OpenAI officially recommends that new applications prioritize the Responses API, so a <strong>coexisting Adapter</strong> is adopted going forward: <code>OpenAiChatModel → /chat/completions</code>, <code>OpenAiStreamingChatModel → /chat/completions + SSE</code>, with <code>OpenAiResponsesModel → /responses</code> to be added in the future. The upper layer still depends only on the Core interfaces, and protocol migration does not intrude into the Agent Runtime.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE02%E3%80%81AgentForge%20%E9%80%82%E9%85%8DOpenAI%E6%8E%A5%E5%85%A5%E6%A0%B8%E5%BF%83%E5%AE%9E%E8%B7%B5#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<p>[1]. <a href="https://developers.openai.com/api/reference/resources/chat/subresources/completions/methods/create" target="_blank" rel="noopener noreferrer" class="">OpenAI Chat Completions API (official reference)</a></p>
<p>[2]. <a href="https://developers.openai.com/api/docs/guides/text" target="_blank" rel="noopener noreferrer" class="">OpenAI Text Generation Guide</a></p>
<p>[3]. Related internal documents: <a class="" href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3">A Quick Understanding of the OpenAI Low-Level Protocol</a>, [ChatModel Core Protocol Layer Design](/blog/01、model模型协议层/chatmodel/统一ChatModel协议层01、ChatModel 核心协议层设计：统一 API 契约封装与多协议扩展实现)</p>
<br>
<p>Compiler: changlu Created: 2026.10.5 Updated: 2026.10.5</p>]]></content:encoded>
            <category>AgentForge</category>
            <category>Model Protocol Layer</category>
            <category>OpenAI</category>
        </item>
        <item>
            <title><![CDATA[OpenAI Protocol 01, A Quick Understanding of the OpenAI Low-Level Protocol]]></title>
            <link>https://agentforge-tech.github.io/AgentForge/en/blog/01、model模型协议层/chatmodel/openai/OpenAI协议01、OpenAI底层协议快速理解</link>
            <guid>https://agentforge-tech.github.io/AgentForge/en/blog/01、model模型协议层/chatmodel/openai/OpenAI协议01、OpenAI底层协议快速理解</guid>
            <pubDate>Sat, 03 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Updated: 2026-10-05]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>Updated: 2026-10-05<br>
<!-- -->Applicable scenario: understanding the OpenAI Chat Completions wire protocol that <code>agentforge-model-openai</code> connects to<br>
<!-- -->Maintainer: changlu</p>
</blockquote>
<!-- -->
<p>One-sentence conclusion: AgentForge's OpenAI Provider connects to <strong>Chat Completions</strong> (<code>POST {baseUrl}/chat/completions</code>). This article only covers "what the protocol itself looks like"; for how AgentForge maps it to the unified <code>ChatRequest</code> / <code>ChatResponse</code>, see "OpenAI Protocol 02, AgentForge OpenAI Integration Core Practices".</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-background-and-problem-introduction">1. Background and Problem Introduction<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#1-background-and-problem-introduction" class="hash-link" aria-label="Direct link to 1. Background and Problem Introduction" title="Direct link to 1. Background and Problem Introduction" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="11-scenario-driven-why-chat-completions-was-chosen-for-the-first-version">1.1 Scenario-Driven: Why Chat Completions Was Chosen for the First Version<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#11-scenario-driven-why-chat-completions-was-chosen-for-the-first-version" class="hash-link" aria-label="Direct link to 1.1 Scenario-Driven: Why Chat Completions Was Chosen for the First Version" title="Direct link to 1.1 Scenario-Driven: Why Chat Completions Was Chosen for the First Version" translate="no">​</a></h2>
<blockquote>
<p>When unifying access to multiple large models for AgentForge, we ran into a real problem: OpenAI officially recommends the new <strong>Responses API</strong>, while at the same time there are a large number of OpenAI-compatible services (self-built gateways, domestic models) that only support <code>/chat/completions</code>.</p>
</blockquote>
<p>Therefore AgentForge release_1.x chose <strong>Chat Completions</strong> as the first version of the unified integration protocol: it is still an API that clearly exists, and it has the broadest compatibility.</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="12-problem-guidance-what-exactly-is-sent-in-a-single-conversation-call">1.2 Problem Guidance: What Exactly Is Sent in a Single Conversation Call?<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#12-problem-guidance-what-exactly-is-sent-in-a-single-conversation-call" class="hash-link" aria-label="Direct link to 1.2 Problem Guidance: What Exactly Is Sent in a Single Conversation Call?" title="Direct link to 1.2 Problem Guidance: What Exactly Is Sent in a Single Conversation Call?" translate="no">​</a></h2>
<blockquote>
<p><strong>Question</strong>: When an Agent calls an OpenAI model once, what do the request body, response body, and streaming SSE look like respectively? How are tool calls (function calling) expressed and filled back on the wire?</p>
</blockquote>
<p>This article explains the protocol in the order of "request → response → streaming → tool calls".</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="13-protocol-positioning">1.3 Protocol Positioning<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#13-protocol-positioning" class="hash-link" aria-label="Direct link to 1.3 Protocol Positioning" title="Direct link to 1.3 Protocol Positioning" translate="no">​</a></h2>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">POST {baseUrl}/chat/completions</span><br></div></code></pre></div></div>
<p>By default:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">baseUrl = https://api.openai.com/v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">→ actual address https://api.openai.com/v1/chat/completions</span><br></div></code></pre></div></div>
<p>Official references:</p>
<ul>
<li class=""><a href="https://developers.openai.com/api/reference/resources/chat/subresources/completions/methods/create" target="_blank" rel="noopener noreferrer" class="">https://developers.openai.com/api/reference/resources/chat/subresources/completions/methods/create</a></li>
<li class=""><a href="https://developers.openai.com/api/docs/guides/text" target="_blank" rel="noopener noreferrer" class="">https://developers.openai.com/api/docs/guides/text</a></li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-request-protocol">2. Request Protocol<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#2-request-protocol" class="hash-link" aria-label="Direct link to 2. Request Protocol" title="Direct link to 2. Request Protocol" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-headers">2.1 Headers<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#21-headers" class="hash-link" aria-label="Direct link to 2.1 Headers" title="Direct link to 2.1 Headers" translate="no">​</a></h2>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Content-Type: application/json</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Accept: application/json</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Authorization: Bearer ${apiKey}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: Authentication uses the standard <code>Authorization: Bearer</code>; enterprise gateways can add custom Headers (trace / tenant / routing).</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-request-body-messages-core">2.2 Request Body: messages (Core)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#22-request-body-messages-core" class="hash-link" aria-label="Direct link to 2.2 Request Body: messages (Core)" title="Direct link to 2.2 Request Body: messages (Core)" translate="no">​</a></h2>
<table><thead><tr><th>role</th><th>Key field</th><th>Meaning</th></tr></thead><tbody><tr><td><code>system</code></td><td><code>content</code></td><td>System prompt</td></tr><tr><td><code>user</code></td><td><code>content</code> or <code>content[]</code></td><td>User input (an array when multimodal)</td></tr><tr><td><code>assistant</code></td><td><code>content</code> / <code>tool_calls[]</code></td><td>Model reply, may carry tool calls</td></tr><tr><td><code>tool</code></td><td><code>tool_call_id</code> + <code>content</code></td><td>Tool execution result fill-back</td></tr></tbody></table>
<p>Plain text example:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"model"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"your-model"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"messages"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"system"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"You are a concise Java assistant."</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"user"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"What is CAS?"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="23-request-body-sampling-and-tool-parameters">2.3 Request Body: Sampling and Tool Parameters<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#23-request-body-sampling-and-tool-parameters" class="hash-link" aria-label="Direct link to 2.3 Request Body: Sampling and Tool Parameters" title="Direct link to 2.3 Request Body: Sampling and Tool Parameters" translate="no">​</a></h2>
<table><thead><tr><th>Field</th><th>Description</th></tr></thead><tbody><tr><td><code>temperature</code></td><td>Sampling temperature</td></tr><tr><td><code>max_tokens</code></td><td>Maximum generated tokens</td></tr><tr><td><code>top_p</code></td><td>Nucleus sampling</td></tr><tr><td><code>stop</code></td><td>Stop sequences</td></tr><tr><td><code>tools[]</code></td><td>Tool declarations: <code>{"type":"function","function":{name,description,parameters,strict}}</code></td></tr><tr><td><code>tool_choice</code></td><td><code>"auto"</code> / <code>"none"</code> / <code>"required"</code> / <code>{"type":"function","function":{"name":X}}</code></td></tr></tbody></table>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="24-complete-non-streaming-request-example">2.4 Complete Non-Streaming Request Example<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#24-complete-non-streaming-request-example" class="hash-link" aria-label="Direct link to 2.4 Complete Non-Streaming Request Example" title="Direct link to 2.4 Complete Non-Streaming Request Example" translate="no">​</a></h2>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"model"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"your-model"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"messages"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"system"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"You are a concise Java assistant."</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"user"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"What is CAS?"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"temperature"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">0.2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"max_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">1024</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"top_p"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">0.9</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-response-protocol">3. Response Protocol<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#3-response-protocol" class="hash-link" aria-label="Direct link to 3. Response Protocol" title="Direct link to 3. Response Protocol" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-non-streaming-chatcompletion">3.1 Non-Streaming chat.completion<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#31-non-streaming-chatcompletion" class="hash-link" aria-label="Direct link to 3.1 Non-Streaming chat.completion" title="Direct link to 3.1 Non-Streaming chat.completion" translate="no">​</a></h2>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"chatcmpl_xxx"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"object"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"chat.completion"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"created"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">1780000000</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"model"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"your-model"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"choices"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"index"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"CAS means Compare-And-Swap."</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"finish_reason"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"stop"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"usage"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"prompt_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">20</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"completion_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">10</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"total_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">30</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Note</strong>: <code>choices</code> is an array; consumers typically only take <code>choices[0]</code>.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-finish_reason">3.2 finish_reason<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#32-finish_reason" class="hash-link" aria-label="Direct link to 3.2 finish_reason" title="Direct link to 3.2 finish_reason" translate="no">​</a></h2>
<table><thead><tr><th>Value</th><th>Meaning</th></tr></thead><tbody><tr><td><code>stop</code></td><td>Normal completion</td></tr><tr><td><code>length</code></td><td>Reached max_tokens</td></tr><tr><td><code>tool_calls</code></td><td>A tool needs to be executed</td></tr><tr><td><code>function_call</code></td><td>Legacy field (deprecated)</td></tr><tr><td><code>content_filter</code></td><td>Content was filtered</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-streaming-protocol-sse">4. Streaming Protocol (SSE)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#4-streaming-protocol-sse" class="hash-link" aria-label="Direct link to 4. Streaming Protocol (SSE)" title="Direct link to 4. Streaming Protocol (SSE)" translate="no">​</a></h2>
<p>Add to the request:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"stream"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"stream_options"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"include_usage"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The response is SSE:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"choices":[{"index":0,"delta":{"content":"Hel"},"finish_reason":null}]}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"choices":[{"index":0,"delta":{"content":"lo"}}]}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"choices":[],"usage":{"prompt_tokens":20,"completion_tokens":2,"total_tokens":22}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: [DONE]</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: When <code>stream_options.include_usage=true</code>, an extra chunk with <code>choices=[]</code> carrying the overall <code>usage</code> appears before <code>[DONE]</code>; during parsing you should read usage first, then determine whether choices is empty.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="41-streaming-increments-of-tool-calls-aggregate-by-index">4.1 Streaming Increments of Tool Calls (Aggregate by index)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#41-streaming-increments-of-tool-calls-aggregate-by-index" class="hash-link" aria-label="Direct link to 4.1 Streaming Increments of Tool Calls (Aggregate by index)" title="Direct link to 4.1 Streaming Increments of Tool Calls (Aggregate by index)" translate="no">​</a></h2>
<p><code>delta.tool_calls[]</code> is delivered in fragments and must be <strong>aggregated by <code>index</code></strong>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">chunk: {"index":0,"id":"call_1","function":{"name":"getWeather","arguments":""}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">chunk: {"index":0,"function":{"arguments":"{\"city\":"}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">chunk: {"index":0,"function":{"arguments":"\"hangzhou\"}"}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼ merge by index</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">ToolRequest(id=call_1, name=getWeather, arguments={"city":"hangzhou"})</span><br></div></code></pre></div></div>
<table><thead><tr><th>delta field</th><th>Aggregation method</th></tr></thead><tbody><tr><td><code>index</code></td><td>Bucket key</td></tr><tr><td><code>id</code></td><td>Overwrite into the current bucket (the first chunk carries the full id)</td></tr><tr><td><code>function.name</code></td><td>Append</td></tr><tr><td><code>function.arguments</code></td><td>Append the raw text, preserving the JSON text</td></tr></tbody></table>
<blockquote>
<p><strong>Note</strong>: Some compatible gateways do not deliver <code>index</code>; in that case use "a new non-empty <code>id</code> appears" as the start of a new call (a fallback counter buckets it), to avoid incorrectly splitting the argument increments of the same call into multiple calls.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-tool-calling-function-calling-wire-details">5. Tool Calling (Function Calling) Wire Details<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#5-tool-calling-function-calling-wire-details" class="hash-link" aria-label="Direct link to 5. Tool Calling (Function Calling) Wire Details" title="Direct link to 5. Tool Calling (Function Calling) Wire Details" translate="no">​</a></h2>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="51-the-assistant-initiates-a-call">5.1 The assistant Initiates a Call<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#51-the-assistant-initiates-a-call" class="hash-link" aria-label="Direct link to 5.1 The assistant Initiates a Call" title="Direct link to 5.1 The assistant Initiates a Call" translate="no">​</a></h2>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token null keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"tool_calls"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"call_1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"function"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"function"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"getWeather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"arguments"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"{\"city\":\"hangzhou\"}"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: <code>arguments</code> is a <strong>string</strong> (JSON text), not an object—this is the key to the subsequent AgentForge normalization and re-parsing.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="52-tool-result-fill-back">5.2 tool Result Fill-Back<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#52-tool-result-fill-back" class="hash-link" aria-label="Direct link to 5.2 tool Result Fill-Back" title="Direct link to 5.2 tool Result Fill-Back" translate="no">​</a></h2>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tool"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"tool_call_id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"call_1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"{\"temperature\":22}"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p><code>tool_call_id</code> must correspond one-to-one with the <code>id</code> at initiation time.</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="53-multiple-tool-calls">5.3 Multiple Tool Calls<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#53-multiple-tool-calls" class="hash-link" aria-label="Direct link to 5.3 Multiple Tool Calls" title="Direct link to 5.3 Multiple Tool Calls" translate="no">​</a></h2>
<p>A single response can return multiple <code>tool_calls</code>, which need to be executed one by one and filled back separately with the <code>tool</code> role, with <code>id</code> matching one-to-one.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-complete-call-walkthrough-user-prompt--function-calls-curl">6. Complete Call Walkthrough: user prompt + function calls (curl)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#6-complete-call-walkthrough-user-prompt--function-calls-curl" class="hash-link" aria-label="Direct link to 6. Complete Call Walkthrough: user prompt + function calls (curl)" title="Direct link to 6. Complete Call Walkthrough: user prompt + function calls (curl)" translate="no">​</a></h2>
<p>Scenario: The user asks <strong>"What's the weather like in Hangzhou today?"</strong> and declares a <code>get_weather</code> tool. Below, from curl construction to response, both non-streaming and streaming are demonstrated (including the second round of tool result fill-back).</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="61-declare-tools">6.1 Declare tools<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#61-declare-tools" class="hash-link" aria-label="Direct link to 6.1 Declare tools" title="Direct link to 6.1 Declare tools" translate="no">​</a></h2>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token property">"tools"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"function"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"function"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"description"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"查询指定城市的当前天气"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"parameters"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"object"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"properties"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"city"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"string"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"description"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"城市名"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"required"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"city"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="62-non-streaming--round-one-the-model-decides-to-call-a-tool">6.2 Non-Streaming · Round One: The Model Decides to Call a Tool<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#62-non-streaming--round-one-the-model-decides-to-call-a-tool" class="hash-link" aria-label="Direct link to 6.2 Non-Streaming · Round One: The Model Decides to Call a Tool" title="Direct link to 6.2 Non-Streaming · Round One: The Model Decides to Call a Tool" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> https://api.openai.com/v1/chat/completions </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Authorization: Bearer </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$OPENAI_API_KEY</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Content-Type: application/json"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-d</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'{</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "model": "gpt-4o-mini",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "messages": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      { "role": "user", "content": "杭州今天天气怎么样？" }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "tools": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      { "type": "function", "function": {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "name": "get_weather",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "description": "查询指定城市的当前天气",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "parameters": { "type": "object",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "properties": { "city": { "type": "string", "description": "城市名" } },</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "required": ["city"] } } }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "tool_choice": "auto"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">  }'</span><br></div></code></pre></div></div>
<p><strong>Return (note <code>finish_reason=tool_calls</code>, <code>content=null</code>):</strong></p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"chatcmpl_abc"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"object"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"chat.completion"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"created"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">1780000000</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"model"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"gpt-4o-mini"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"choices"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"index"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token null keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"tool_calls"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"call_1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"function"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token property">"function"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"arguments"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"{\"city\":\"杭州\"}"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"finish_reason"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tool_calls"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"usage"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"prompt_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">62</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"completion_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">15</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"total_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">77</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: <code>arguments</code> is the string <code>"{\"city\":\"杭州\"}"</code>; after local parsing, execute <code>get_weather("杭州")</code>.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="63-non-streaming--round-two-fill-back-the-tool-result-to-get-the-final-answer">6.3 Non-Streaming · Round Two: Fill Back the Tool Result to Get the Final Answer<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#63-non-streaming--round-two-fill-back-the-tool-result-to-get-the-final-answer" class="hash-link" aria-label="Direct link to 6.3 Non-Streaming · Round Two: Fill Back the Tool Result to Get the Final Answer" title="Direct link to 6.3 Non-Streaming · Round Two: Fill Back the Tool Result to Get the Final Answer" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> https://api.openai.com/v1/chat/completions </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Authorization: Bearer </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$OPENAI_API_KEY</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Content-Type: application/json"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-d</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'{</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "model": "gpt-4o-mini",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "messages": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      { "role": "user", "content": "杭州今天天气怎么样？" },</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      { "role": "assistant", "content": null,</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        "tool_calls": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          { "id": "call_1", "type": "function",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "function": { "name": "get_weather", "arguments": "{\"city\":\"杭州\"}" } } ] },</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      { "role": "tool", "tool_call_id": "call_1",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        "content": "{\"temperature\":26,\"text\":\"晴\"}" }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">  }'</span><br></div></code></pre></div></div>
<p><strong>Return (<code>finish_reason=stop</code>):</strong></p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"chatcmpl_def"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"choices"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"index"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"杭州今天 26℃，天气晴。"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"finish_reason"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"stop"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"usage"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"prompt_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">85</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"completion_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">12</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"total_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">97</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="64-streaming--round-one-tool_calls-increments-streamtrue">6.4 Streaming · Round One: tool_calls Increments (<code>stream:true</code>)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#64-streaming--round-one-tool_calls-increments-streamtrue" class="hash-link" aria-label="Direct link to 64-streaming--round-one-tool_calls-increments-streamtrue" title="Direct link to 64-streaming--round-one-tool_calls-increments-streamtrue" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> https://api.openai.com/v1/chat/completions </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Authorization: Bearer </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$OPENAI_API_KEY</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Content-Type: application/json"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-d</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'{</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "model": "gpt-4o-mini",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "stream": true,</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "stream_options": { "include_usage": true },</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "messages": [ { "role": "user", "content": "杭州今天天气怎么样？" } ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "tools": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      { "type": "function", "function": {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "name": "get_weather", "description": "查询指定城市的当前天气",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "parameters": { "type": "object",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "properties": { "city": { "type": "string" } }, "required": ["city"] } } }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "tool_choice": "auto"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">  }'</span><br></div></code></pre></div></div>
<p><strong>SSE (in arrival order):</strong></p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_1","type":"function","function":{"name":"get_weather","arguments":""}}]},"finish_reason":null}]}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"city\":"}}]},"finish_reason":null}]}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"杭州\"}"}}]},"finish_reason":null}]}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}]}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"choices":[],"usage":{"prompt_tokens":62,"completion_tokens":15,"total_tokens":77}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: [DONE]</span><br></div></code></pre></div></div>
<p><strong>Aggregation</strong> (bucket by <code>tool_calls[].index</code>; <code>id</code> overwrites, <code>name</code>/<code>arguments</code> append):</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">index=0 → id=call_1, name=get_weather, arguments={"city":"杭州"}</span><br></div></code></pre></div></div>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="65-streaming--round-two-generate-the-final-text-after-fill-back">6.5 Streaming · Round Two: Generate the Final Text After Fill-Back<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#65-streaming--round-two-generate-the-final-text-after-fill-back" class="hash-link" aria-label="Direct link to 6.5 Streaming · Round Two: Generate the Final Text After Fill-Back" title="Direct link to 6.5 Streaming · Round Two: Generate the Final Text After Fill-Back" translate="no">​</a></h2>
<p>The request body is the same as 6.3, with <code>"stream": true, "stream_options": {"include_usage": true}</code> appended.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"choices":[{"index":0,"delta":{"role":"assistant","content":"杭州"},"finish_reason":null}]}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"choices":[{"index":0,"delta":{"content":"今天 26℃"},"finish_reason":null}]}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"choices":[{"index":0,"delta":{"content":"，天气晴。"},"finish_reason":null}]}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"choices":[],"usage":{"prompt_tokens":85,"completion_tokens":12,"total_tokens":97}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: [DONE]</span><br></div></code></pre></div></div>
<p><strong>Aggregation</strong>: concatenate <code>delta.content</code> in order → <code>"杭州今天 26℃，天气晴。"</code>.</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="66-walkthrough-key-points-summary">6.6 Walkthrough Key Points Summary<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#66-walkthrough-key-points-summary" class="hash-link" aria-label="Direct link to 6.6 Walkthrough Key Points Summary" title="Direct link to 6.6 Walkthrough Key Points Summary" translate="no">​</a></h2>
<table><thead><tr><th>Observation point</th><th>Non-streaming</th><th>Streaming</th></tr></thead><tbody><tr><td>Tool arguments</td><td><code>message.tool_calls[].function.arguments</code> (string)</td><td><code>delta.tool_calls[].function.arguments</code> fragmented, aggregated by <code>index</code></td></tr><tr><td>Whether a tool is sent</td><td><code>finish_reason == "tool_calls"</code></td><td><code>finish_reason == "tool_calls"</code> of the last chunk</td></tr><tr><td>Text</td><td><code>message.content</code></td><td><code>delta.content</code> returned segment by segment</td></tr><tr><td>usage</td><td>Response body <code>usage</code></td><td>The usage chunk with <code>choices=[]</code> at the end</td></tr><tr><td>End</td><td>—</td><td><code>data: [DONE]</code></td></tr></tbody></table>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="67-real-test-verification">6.7 Real Test Verification<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#67-real-test-verification" class="hash-link" aria-label="Direct link to 6.7 Real Test Verification" title="Direct link to 6.7 Real Test Verification" translate="no">​</a></h2>
<p>You can run the script in the repository directly (an API Key needs to be set):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token builtin class-name" style="color:rgb(189, 147, 249)">export</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">OPENAI_API_KEY</span><span class="token operator">=</span><span class="token plain">sk-</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token plain">.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">bash</span><span class="token plain"> docs/dev/backend/AgentForge核心模块设计原理/01、model模型协议层/chatmodel/openai/verify-openai-chat.sh</span><br></div></code></pre></div></div>
<p>The script executes in order: ① a non-streaming tool-calling request; ② printing <code>tool_calls</code> with <code>jq</code>; ③ a streaming request printing SSE. Optional environment variables <code>OPENAI_BASE_URL</code> / <code>OPENAI_MODEL</code> override the defaults.</p>
<blockquote>
<p><strong>Note</strong>: Real calls incur charges; do not write the Key into the script or commit it to the repository.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-summary">7. Summary<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#7-summary" class="hash-link" aria-label="Direct link to 7. Summary" title="Direct link to 7. Summary" translate="no">​</a></h2>
<ol>
<li class=""><strong>Endpoint</strong>: <code>POST {baseUrl}/chat/completions</code>, authenticated with <code>Authorization: Bearer</code>.</li>
<li class=""><strong>Request</strong>: <code>messages</code> (system / user / assistant / tool) + sampling parameters + <code>tools</code> / <code>tool_choice</code>.</li>
<li class=""><strong>Response</strong>: <code>choices[0].message</code> (<code>content</code> or <code>tool_calls</code>) + <code>usage</code> + <code>finish_reason</code>.</li>
<li class=""><strong>Streaming</strong>: SSE <code>data:</code> chunk; <code>delta.content</code> text, <code>delta.tool_calls</code> aggregated by <code>index</code>; ends with <code>[DONE]</code>.</li>
<li class=""><strong>Tools</strong>: <code>assistant.tool_calls</code> (<code>arguments</code> is a string) → <code>tool</code> role filled back by <code>tool_call_id</code>.</li>
</ol>
<blockquote>
<p>For how AgentForge maps these fields to unified types, see "OpenAI Protocol 02, AgentForge OpenAI Integration Core Practices".</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<p>[1]. <a href="https://developers.openai.com/api/reference/resources/chat/subresources/completions/methods/create" target="_blank" rel="noopener noreferrer" class="">OpenAI Chat Completions API (official reference)</a></p>
<p>[2]. <a href="https://developers.openai.com/api/docs/guides/text" target="_blank" rel="noopener noreferrer" class="">OpenAI Text Generation Guide</a></p>
<p>[3]. <a href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events" target="_blank" rel="noopener noreferrer" class="">Server-Sent Events (MDN)</a></p>
<p>[4]. Related internal document: [AgentForge OpenAI Integration Core Practices](/blog/01、model模型协议层/chatmodel/openai/OpenAI协议02、AgentForge 适配OpenAI接入核心实践)</p>
<br>
<p>Compiler: changlu Created: 2026.10.5 Updated: 2026.10.5</p>]]></content:encoded>
            <category>AgentForge</category>
            <category>Model Protocol Layer</category>
            <category>OpenAI</category>
        </item>
        <item>
            <title><![CDATA[AgentForge Model Protocol Layer ChatModel Principles 02, Function Calling Tool Call Transformation and Execution Extension Implementation: Tool Contract + ToolService Execution Loop]]></title>
            <link>https://agentforge-tech.github.io/AgentForge/en/blog/01、model模型协议层/chatmodel/统一ChatModel协议层02、Function Calling 工具调用转换与执行扩展实现：Tool 契约 + ToolService 执行循环</link>
            <guid>https://agentforge-tech.github.io/AgentForge/en/blog/01、model模型协议层/chatmodel/统一ChatModel协议层02、Function Calling 工具调用转换与执行扩展实现：Tool 契约 + ToolService 执行循环</guid>
            <pubDate>Fri, 02 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Applicable version: release_1.x]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>Applicable version: release_1.x<br>
<!-- -->Applicable modules: <code>agentforge-model-api</code> (<code>cloud.changlu.agentforge.model.tool</code>) + <code>agentforge-model-core</code><br>
<!-- -->Package prefix: <code>cloud.changlu.agentforge.model.tool</code><br>
<!-- -->Maintainer: Changlu</p>
</blockquote>
<!-- -->
<p>Series positioning: This is article 02 in the ChatModel category principles course. Article 01, "ChatModel Core Protocol Layer Design: Unified API Contract Encapsulation and Multi-Protocol Extension Implementation," covered "how the model talks" — unified input/output and multi-protocol extension; this article covers "how the model takes action" — the complete closed loop of Function Calling from <strong>tool declaration</strong> and <strong>protocol transformation</strong> to <strong>execution callback</strong>.</p>
<blockquote>
<p>The full text focuses on the <code>tool</code> package of <code>agentforge-model-api</code> (top-level contracts + the <code>spec</code> / <code>execution</code> / <code>error</code> subpackages) as well as
the default implementations in <code>agentforge-model-core</code> (<code>ToolSpecifications</code> / <code>DefaultToolExecutor</code> / <code>ToolService</code>).
For how the Provider side maps "<code>tool_calls</code> / <code>tool_use</code> into <code>AiMessage</code>," please see Chapter 4 of Article 01.</p>
</blockquote>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="document-navigation">Document Navigation<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#document-navigation" class="hash-link" aria-label="Direct link to Document Navigation" title="Direct link to Document Navigation" translate="no">​</a></h2>
<table><thead><tr><th>Document</th><th>Description</th></tr></thead><tbody><tr><td>Current article (Function Calling / Tool)</td><td><code>@Tool</code> declaration, <code>ToolSpecification</code>, reflective execution, <code>ToolService</code> execution loop and error handling</td></tr><tr><td>[ChatModel Principles 01, ChatModel Core Protocol Layer Design: Unified API Contract Encapsulation and Multi-Protocol Extension Implementation.md](/blog/01、model模型协议层/chatmodel/统一ChatModel协议层01、ChatModel 核心协议层设计：统一 API 契约封装与多协议扩展实现)</td><td>Article 01: unified API contract, standard ChatModel protocol, multi-protocol extension</td></tr><tr><td>[openai/OpenAI Protocol 02, AgentForge OpenAI Integration Core Practice.md](/blog/01、model模型协议层/chatmodel/openai/OpenAI协议02、AgentForge 适配OpenAI接入核心实践)</td><td>OpenAI Provider's <code>tool_calls</code> wire mapping and streaming aggregation</td></tr><tr><td>[anthropic/Anthropic Protocol 02, AgentForge Anthropic Integration Core Practice.md](/blog/01、model模型协议层/chatmodel/anthropic/Anthropic协议02、AgentForge 适配Anthropic接入核心实践)</td><td>Anthropic Provider's <code>tool_use</code> / <code>tool_result</code> wire mapping</td></tr></tbody></table>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="table-of-contents">Table of Contents<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#table-of-contents" class="hash-link" aria-label="Direct link to Table of Contents" title="Direct link to Table of Contents" translate="no">​</a></h2>
<ul>
<li class=""><a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#1-background-after-the-model-says-i-want-to-call-a-tool-who-executes-it" class="">1. Background: After the Model Says "I Want to Call a Tool," Who Executes It?</a></li>
<li class=""><a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#2-core-concepts-what-contracts-make-up-the-tool-package" class="">2. Core Concepts: What Contracts Make Up the tool Package</a></li>
<li class=""><a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#3-implementation-approach-from-method-declaration-to-tool-specification" class="">3. Implementation Approach: From Method Declaration to Tool Specification</a></li>
<li class=""><a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#4-toolservice-the-reasoning-execution-loop" class="">4. ToolService: The Reasoning-Execution Loop</a></li>
<li class=""><a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#5-practical-example-real-curl-and-returned-results" class="">5. Practical Example: Real curl and Returned Results</a></li>
<li class=""><a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#6-verification-tests" class="">6. Verification Tests</a></li>
<li class=""><a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#7-boundaries-compatibility-and-evolution" class="">7. Boundaries, Compatibility and Evolution</a></li>
<li class=""><a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#8-summary" class="">8. Summary</a></li>
<li class=""><a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#references" class="">References</a></li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-background-after-the-model-says-i-want-to-call-a-tool-who-executes-it">1. Background: After the Model Says "I Want to Call a Tool," Who Executes It?<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#1-background-after-the-model-says-i-want-to-call-a-tool-who-executes-it" class="hash-link" aria-label="Direct link to 1. Background: After the Model Says &quot;I Want to Call a Tool,&quot; Who Executes It?" title="Direct link to 1. Background: After the Model Says &quot;I Want to Call a Tool,&quot; Who Executes It?" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="11-what-is-function-calling-core-concept">1.1 What Is Function Calling (Core Concept)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#11-what-is-function-calling-core-concept" class="hash-link" aria-label="Direct link to 1.1 What Is Function Calling (Core Concept)" title="Direct link to 1.1 What Is Function Calling (Core Concept)" translate="no">​</a></h3>
<p>In one sentence: <strong>Function Calling (also called Tool Calling) lets the model, while generating a response, "propose" calling some external function, which the application executes, and then hand the result back to the model to continue generating.</strong></p>
<p>OpenAI's official <a href="https://developers.openai.com/api/docs/guides/function-calling" target="_blank" rel="noopener noreferrer" class="">"Function calling" guide</a> defines it as: Function Calling provides a powerful and flexible way for models to connect to external systems and access data and actions beyond the training data.</p>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="111-three-core-terms">1.1.1 Three Core Terms<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#111-three-core-terms" class="hash-link" aria-label="Direct link to 1.1.1 Three Core Terms" title="Direct link to 1.1.1 Three Core Terms" translate="no">​</a></h4>
<table><thead><tr><th>Term</th><th>English</th><th>Meaning</th></tr></thead><tbody><tr><td>Tool / Function</td><td>Function / Tool</td><td>A piece of capability we tell the model "you can call," usually with a JSON-Schema parameter definition</td></tr><tr><td>Tool Call</td><td>Function Call / Tool Call</td><td>The structured response the model returns when it decides a tool needs to be called (containing tool name and arguments)</td></tr><tr><td>Tool Result</td><td>Function Call Output / Tool Call Output</td><td>The result produced after the application executes the tool, which may be JSON / text / image and references the corresponding call ID</td></tr></tbody></table>
<blockquote>
<p><strong>Note</strong>: The model itself does <strong>not execute</strong> any tool; it is only responsible for "proposing the call." The actual execution always happens on the application side (client tool) or the vendor side (server tool, see 1.1.3).</p>
</blockquote>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="112-five-step-call-flow">1.1.2 Five-Step Call Flow<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#112-five-step-call-flow" class="hash-link" aria-label="Direct link to 1.1.2 Five-Step Call Flow" title="Direct link to 1.1.2 Five-Step Call Flow" translate="no">​</a></h4>
<p>Referring to OpenAI's <a href="https://developers.openai.com/api/docs/guides/function-calling" target="_blank" rel="noopener noreferrer" class="">"Function calling" guide</a>, Tool Calling is a multi-step conversation between the application and the model:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">1. 携带「模型可调用的工具列表」发起第一次请求</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">2. 从模型收到一次 tool call（工具名 + 参数）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">3. 在应用侧执行代码（使用 tool call 的输入）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">4. 把执行结果（tool output）连同历史消息再发给模型</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">5. 收到模型的最终回答（或又一次 tool call）</span><br></div></code></pre></div></div>
<p>This flow can loop multiple times: as long as the model is still requesting tools, it continues to "execute → fill back → call again" until the model gives a final answer that no longer needs tools. This is exactly the loop that <code>ToolService</code> needs to encapsulate.</p>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="113-client-tool-vs-server-tool">1.1.3 Client Tool vs Server Tool<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#113-client-tool-vs-server-tool" class="hash-link" aria-label="Direct link to 1.1.3 Client Tool vs Server Tool" title="Direct link to 1.1.3 Client Tool vs Server Tool" translate="no">​</a></h4>
<p>Different vendors divide "who executes the tool" slightly differently. Anthropic, in
<a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview" target="_blank" rel="noopener noreferrer" class="">"Tool use with Claude"</a>, explicitly
distinguishes two categories:</p>
<table><thead><tr><th>Type</th><th>Executor</th><th>Typical Example</th><th>Protocol Representation</th></tr></thead><tbody><tr><td>Client Tool</td><td>Your application</td><td>Custom <code>get_weather</code></td><td>The model returns <code>stop_reason: "tool_use"</code> + a <code>tool_use</code> block, and the application responds with <code>tool_result</code> after execution</td></tr><tr><td>Server Tool</td><td>Vendor infrastructure</td><td>Web Search, Code Execution</td><td>The vendor executes directly, and the result is returned with the same response</td></tr></tbody></table>
<blockquote>
<p><strong>Key point</strong>: AgentForge's <code>tool</code> package focuses on <strong>Client Tool</strong> — the most common chain of "the model proposes, the application executes";
for upper layers, a Server Tool is equivalent to "the model gave the result directly" and does not go through <code>ToolService</code>.</p>
</blockquote>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="114-why-the-model-calls-tools">1.1.4 Why the Model "Calls Tools"<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#114-why-the-model-calls-tools" class="hash-link" aria-label="Direct link to 1.1.4 Why the Model &quot;Calls Tools&quot;" title="Direct link to 1.1.4 Why the Model &quot;Calls Tools&quot;" translate="no">​</a></h4>
<p>The model is not really running code; it is <strong>predicting a structured output</strong>. Once tools (name + description + JSON-Schema parameters) are provided in the request, the model decides based on user intent:</p>
<ul>
<li class="">Whether to call a tool (<code>tool_choice</code> can control <code>auto</code> / <code>required</code> / a specific tool);</li>
<li class="">Which tool to call (tool name and description are key clues);</li>
<li class="">What arguments to use (generate JSON according to the Schema).</li>
</ul>
<p>A <a href="https://arxiv.org/abs/2409.18807" target="_blank" rel="noopener noreferrer" class="">survey on tool learning (LLM With Tools: A Survey)</a> summarizes this kind of capability as
"mapping user instructions into executable plans and executing them," whose core challenges are precisely: <strong>the timing of tool calls, the accuracy of tool selection, and a robust reasoning process</strong>. This is also the fundamental reason AgentForge standardizes "tool declaration" into a Schema and turns "execution" into a replaceable engine.</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="12-starting-from-a-concrete-scenario">1.2 Starting from a Concrete Scenario<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#12-starting-from-a-concrete-scenario" class="hash-link" aria-label="Direct link to 1.2 Starting from a Concrete Scenario" title="Direct link to 1.2 Starting from a Concrete Scenario" translate="no">​</a></h3>
<p>When writing a "check the weather" Agent, we want the user to ask "What's the weather in Hangzhou today?" and the model not to make things up, but to output a tool call so we can look up real data, and then have the model answer based on the result. The content returned by the model in the first round is roughly:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">assistant: tool_calls = [{ id: "call_abc123", name: "get_weather", arguments: "{\"city\":\"杭州\"}" }]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">finish_reason = tool_calls</span><br></div></code></pre></div></div>
<p>The question arises: <strong>Who does the next few steps?</strong></p>
<ol>
<li class="">Parse the JSON in <code>arguments</code> into parameters and reflectively call <code>getWeather("杭州")</code>;</li>
<li class="">Convert the return value to text and wrap it as a <code>tool</code> result message;</li>
<li class="">Send "user question + assistant tool call + tool result" back to the model together;</li>
<li class="">Execute repeatedly until the model no longer requests tools.</li>
</ol>
<p>If every Agent business hand-writes these four steps, the end result is a pile of repetitive, error-prone, hard-to-test loop code.</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="13-what-happens-if-we-dont-encapsulate-this-layer">1.3 What Happens If We Don't Encapsulate This Layer?<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#13-what-happens-if-we-dont-encapsulate-this-layer" class="hash-link" aria-label="Direct link to 1.3 What Happens If We Don't Encapsulate This Layer?" title="Direct link to 1.3 What Happens If We Don't Encapsulate This Layer?" translate="no">​</a></h3>
<p>In the project we compared two approaches, "hand-writing the loop" and "providing an execution engine":</p>
<table><thead><tr><th>Approach</th><th>Advantages</th><th>Drawbacks</th></tr></thead><tbody><tr><td>Hand-writing the 4-step loop in business code</td><td>Intuitive, no extra abstraction</td><td>Repeated parsing/reflection/filling; error handling written separately each time; very hard to unit test</td></tr><tr><td>Providing only the <code>@Tool</code> annotation</td><td>Simple declaration</td><td>Business still has to scan, build Specifications, and bind arguments itself</td></tr><tr><td><strong>Tool contract + ToolService (current choice)</strong></td><td>Declaration, execution, loop, and error handling are all consolidated</td><td>Requires a stable tool contract and execution engine</td></tr></tbody></table>
<p>The <code>tool</code> package of <code>agentforge-model-api</code> (top-level contracts + the three subpackages <code>spec</code> / <code>execution</code> / <code>error</code>) exists precisely to standardize these four steps:</p>
<blockquote>
<p><strong>Key point</strong>: The Provider layer solves "how tool calls are expressed in the model protocol" (wire mapping); the <code>tool</code> package solves "how the upper-layer Agent turns an ordinary Java method into a tool that an LLM can call, and automatically drives multi-round execution."</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="14-what-this-layer-needs-to-solve">1.4 What This Layer Needs to Solve<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#14-what-this-layer-needs-to-solve" class="hash-link" aria-label="Direct link to 1.4 What This Layer Needs to Solve" title="Direct link to 1.4 What This Layer Needs to Solve" translate="no">​</a></h3>
<ul>
<li class=""><strong>Simple declaration</strong>: One <code>@Tool</code> annotation + one <code>@P</code> annotation turns an ordinary Java method into an LLM-callable tool;</li>
<li class=""><strong>Portable specification</strong>: Turns a method signature into a JSON-Schema-style <code>ToolSpecification</code> that naturally connects to OpenAI's / Anthropic's <code>tools[]</code>;</li>
<li class=""><strong>Replaceable execution</strong>: Reflective execution by default, but also allows a custom <code>ToolExecutor</code> (a Lambda is enough);</li>
<li class=""><strong>Controllable loop</strong>: Built-in reasoning-execution loop, maximum rounds, immediate return, hallucinated-tool and error-handling strategies;</li>
<li class=""><strong>Vendor-neutral</strong>: Whether the underlying provider is OpenAI or Anthropic, the upper layer only programs against <code>ToolExecutionRequest</code> / <code>ToolExecutor</code>.</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="15-current-support-for-function-calling-across-vendors">1.5 Current Support for Function Calling Across Vendors<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#15-current-support-for-function-calling-across-vendors" class="hash-link" aria-label="Direct link to 1.5 Current Support for Function Calling Across Vendors" title="Direct link to 1.5 Current Support for Function Calling Across Vendors" translate="no">​</a></h3>
<p><strong>Guiding question</strong>: Do all large-model vendors support "standard" Function Calling?</p>
<p>Here is the conclusion first: <strong>There is no unique cross-vendor standard, but two de facto standards have formed; moreover, "protocol support" is not the same as "model support."</strong></p>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="151-de-facto-standard-the-openai-compatible-camp">1.5.1 De Facto Standard: The OpenAI-Compatible Camp<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#151-de-facto-standard-the-openai-compatible-camp" class="hash-link" aria-label="Direct link to 1.5.1 De Facto Standard: The OpenAI-Compatible Camp" title="Direct link to 1.5.1 De Facto Standard: The OpenAI-Compatible Camp" translate="no">​</a></h4>
<p>OpenAI Chat Completions' <code>tools[]</code> / <code>tool_choice</code> / <code>tool_calls[]</code> / <code>role=tool</code> has become the industry de facto standard, and a large number of vendors directly "compatibly" implement this format:</p>
<ul>
<li class="">Large-model vendors: DeepSeek, Qwen / DashScope, Moonshot / Kimi, MiniMax, etc.;</li>
<li class="">Inference hosting: Groq, Together, Mistral, etc.;</li>
<li class="">Local / gateway: <a href="https://docs.ollama.com/capabilities/tool-calling" target="_blank" rel="noopener noreferrer" class="">Ollama</a>,
<a href="https://docs.vllm.ai/en/stable/features/tool_calling/" target="_blank" rel="noopener noreferrer" class="">vLLM</a>, Xinference, LiteLLM, etc.</li>
</ul>
<blockquote>
<p><strong>Key point</strong>: This is also why AgentForge's <code>OpenAiChatModel</code> can be reused — as long as the implementer follows the Chat Completions protocol, you can integrate by switching the <code>baseUrl</code>, with no need to add a new Provider (see Chapter 4 of Article 01).</p>
</blockquote>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="152-comparison-of-each-vendors-native-protocol">1.5.2 Comparison of Each Vendor's Native Protocol<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#152-comparison-of-each-vendors-native-protocol" class="hash-link" aria-label="Direct link to 1.5.2 Comparison of Each Vendor's Native Protocol" title="Direct link to 1.5.2 Comparison of Each Vendor's Native Protocol" translate="no">​</a></h4>
<p>Not all vendors follow the OpenAI format. Anthropic and Google have their own native protocols, semantically equivalent but different on the wire:</p>
<table><thead><tr><th>Vendor</th><th>Tool Declaration</th><th>Tool Call Return</th><th>Result Fill-back</th></tr></thead><tbody><tr><td><a href="https://developers.openai.com/api/docs/guides/function-calling" target="_blank" rel="noopener noreferrer" class="">OpenAI (Chat Completions)</a></td><td><code>tools[].function.parameters</code></td><td><code>tool_calls[]</code></td><td><code>role=tool</code> + <code>tool_call_id</code></td></tr><tr><td><a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview" target="_blank" rel="noopener noreferrer" class="">Anthropic (Messages)</a></td><td><code>tools[].input_schema</code></td><td><code>stop_reason=tool_use</code> + <code>tool_use</code> block</td><td><code>tool_result</code> (under <code>role=user</code>)</td></tr><tr><td><a href="https://ai.google.dev/gemini-api/docs/function-calling" target="_blank" rel="noopener noreferrer" class="">Google Gemini</a></td><td><code>functionDeclarations</code> (subset of OpenAPI schema)</td><td><code>functionCall</code></td><td><code>functionResponse</code></td></tr></tbody></table>
<blockquote>
<p><strong>Note</strong>: OpenAI itself is also evolving — the newer Responses API models tools (built-in tools, tool search, etc.) in a way that is not entirely consistent with Chat Completions; when integrating, you need to be clear about which API you are targeting.</p>
</blockquote>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="153-protocol-support--model-support">1.5.3 Protocol Support ≠ Model Support<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#153-protocol-support--model-support" class="hash-link" aria-label="Direct link to 1.5.3 Protocol Support ≠ Model Support" title="Direct link to 1.5.3 Protocol Support ≠ Model Support" translate="no">​</a></h4>
<p>Even if the protocol format is compatible, <strong>what really determines whether tools can be used is the model itself</strong>, and differences show up across multiple dimensions:</p>
<table><thead><tr><th>Dimension</th><th>Example of Difference</th></tr></thead><tbody><tr><td>Whether tool calls are supported</td><td>Some older models and some small-parameter models lack this capability</td></tr><tr><td>Version differences</td><td>Early DeepSeek-R1 did not support tool calls, whereas <a href="https://api-docs.deepseek.com/guides/tool_calls/" target="_blank" rel="noopener noreferrer" class="">DeepSeek-V3</a> does</td></tr><tr><td><code>tool_choice</code> mode</td><td><code>required</code> / specifying a function is rejected in some implementations (DeepSeek has related issues)</td></tr><tr><td>Parallel tool calls</td><td>Whether multiple <code>tool_calls</code> can be returned at once varies by model / endpoint</td></tr><tr><td>Streaming tool increments</td><td>Whether <code>delta.tool_calls[]</code> fragments are supported, or only in the final response</td></tr><tr><td>strict schema</td><td>The degree of support for <code>strict</code> / strict grammar constraints varies</td></tr><tr><td>Reasoning + tools</td><td>Whether the thinking process (<code>reasoning_content</code>) and tool calls can coexist in the same stream</td></tr></tbody></table>
<blockquote>
<p><strong>Drawback note</strong>: Verifying only "wire compatibility" is not enough. With the same OpenAI-compatible endpoint, after switching models, <code>tool_choice=required</code> may error out directly, or parallel tool calls may no longer be returned. When integrating, capability probing should be done for the specific "model + endpoint" rather than assuming full equivalence.</p>
</blockquote>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="154-significance-for-agentforge">1.5.4 Significance for AgentForge<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#154-significance-for-agentforge" class="hash-link" aria-label="Direct link to 1.5.4 Significance for AgentForge" title="Direct link to 1.5.4 Significance for AgentForge" translate="no">​</a></h4>
<p>AgentForge's approach is "internal unified contract + Provider adaptation + explicit degradation":</p>
<ul>
<li class="">Internally, only <code>ToolSpecification</code> / <code>ToolExecutionRequest</code> are exposed, without leaking vendor fields;</li>
<li class="">Each Provider only maps its own protocol (OpenAI <code>tool_calls</code>, Anthropic <code>tool_use</code>);</li>
<li class="">Generic fields not supported by a vendor are handled in the three ways from section 2.4 of Article 01: map / ignore or explicitly fail validation / go through <code>customParameters</code>.</li>
</ul>
<p>Therefore "a certain vendor or model does not fully support something" will not pollute the upper layer — at most it is an explicit rejection by a certain Provider, rather than crashing the Agent's main loop.</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="16-further-reading-and-reference-materials">1.6 Further Reading and Reference Materials<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#16-further-reading-and-reference-materials" class="hash-link" aria-label="Direct link to 1.6 Further Reading and Reference Materials" title="Direct link to 1.6 Further Reading and Reference Materials" translate="no">​</a></h3>
<ul>
<li class=""><a href="https://developers.openai.com/api/docs/guides/function-calling" target="_blank" rel="noopener noreferrer" class="">OpenAI "Function calling" official guide</a>: term definitions
(Function / Tool, Tool Call, Tool Output) and the five-step flow, the main reference for section 1.1 of this article;</li>
<li class=""><a href="https://openai.com/index/function-calling-and-other-api-updates/" target="_blank" rel="noopener noreferrer" class="">OpenAI's June 2023 "Function calling and other API updates"</a>:
the first public release of the Function Calling capability;</li>
<li class=""><a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview" target="_blank" rel="noopener noreferrer" class="">Anthropic "Tool use with Claude"</a>:
the division of Client Tool / Server Tool, and the complete round trip of <code>tool_use</code> / <code>tool_result</code>;</li>
<li class=""><a href="https://arxiv.org/abs/2409.18807" target="_blank" rel="noopener noreferrer" class="">arXiv "LLM With Tools: A Survey" (2409.18807)</a>: methods and challenges of tool learning
(call timing, tool selection, dynamic planning);</li>
<li class=""><a href="https://gorilla.cs.berkeley.edu/leaderboard.html" target="_blank" rel="noopener noreferrer" class="">Berkeley Function Calling Leaderboard (BFCL)</a>: a public leaderboard
measuring model tool-calling accuracy;</li>
<li class=""><a href="https://github.com/langchain4j/langchain4j" target="_blank" rel="noopener noreferrer" class="">LangChain4j</a>: an important reference for AgentForge's <code>tool</code> package in terms of design style.</li>
</ul>
<p>See the "References" section at the end for complete links.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-core-concepts-what-contracts-make-up-the-tool-package">2. Core Concepts: What Contracts Make Up the tool Package<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#2-core-concepts-what-contracts-make-up-the-tool-package" class="hash-link" aria-label="Direct link to 2. Core Concepts: What Contracts Make Up the tool Package" title="Direct link to 2. Core Concepts: What Contracts Make Up the tool Package" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-package-structure-and-api--core-division">2.1 Package Structure and api / core Division<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#21-package-structure-and-api--core-division" class="hash-link" aria-label="Direct link to 2.1 Package Structure and api / core Division" title="Direct link to 2.1 Package Structure and api / core Division" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">cloud.changlu.agentforge.model.tool</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── Tool / P / ReturnBehavior / ToolExecutor            # api：核心契约</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── spec</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── ToolSpecification                               # api：name/description/parameters/strict/metadata</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   └── ToolParameters                                  # api：JSON-Schema 风格参数（纯 Map，无第三方依赖）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── execution</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── ToolExecution                                   # api：一次执行（请求 + 结果 + 耗时）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── ToolExecutionResult                             # api：执行结果值对象（text + 原始对象 + isError）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── ToolService                                     # core：工具注册 + 推理/执行循环</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── DefaultToolExecutor                             # core：@Tool 方法反射执行器</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   └── ToolExecutionRequestUtil                        # core：arguments JSON -&gt; Map</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">└── error</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    ├── ToolArgumentsException / ToolExecutionException # api：两类异常</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    ├── ToolArgumentsErrorHandler / ToolExecutionErrorHandler  # api：错误处理器</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    ├── ToolErrorContext                                # api：错误上下文</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    └── ToolErrorHandlerResult                          # api：可回给 LLM 的错误结果</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Note</strong>: <code>Tool</code> / <code>P</code> / <code>ReturnBehavior</code> / <code>ToolExecutor</code> / the value objects of <code>spec</code> / <code>execution</code> / <code>error</code>
are all in <strong>api</strong> (contracts); <code>ToolService</code> / <code>DefaultToolExecutor</code> / <code>ToolExecutionRequestUtil</code> /
<code>ToolSpecifications</code> are in <strong>core</strong> (default implementations).</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-four-categories-of-core-contracts">2.2 Four Categories of Core Contracts<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#22-four-categories-of-core-contracts" class="hash-link" aria-label="Direct link to 2.2 Four Categories of Core Contracts" title="Direct link to 2.2 Four Categories of Core Contracts" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">tool 包契约</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── 声明契约：@Tool / @P / ToolSpecification / ToolParameters</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── 执行契约：ToolExecutor（execute / executeWithResult）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── 结果契约：ToolExecution / ToolExecutionResult / ReturnBehavior</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">└── 错误契约：ToolArgumentsException / ToolExecutionException + ErrorHandler + ToolErrorHandlerResult</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-implementation-approach-from-method-declaration-to-tool-specification">3. Implementation Approach: From Method Declaration to Tool Specification<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#3-implementation-approach-from-method-declaration-to-tool-specification" class="hash-link" aria-label="Direct link to 3. Implementation Approach: From Method Declaration to Tool Specification" title="Direct link to 3. Implementation Approach: From Method Declaration to Tool Specification" translate="no">​</a></h2>
<p><strong>Guiding question</strong>: How does an ordinary Java method, step by step, become a "tool" that the model can understand and the framework can execute?</p>
<p>The overall design is divided into two steps:</p>
<ol>
<li class=""><strong>First align with standard Function Calling</strong>: Whether OpenAI, Anthropic, or Gemini, the interaction paradigm on the model side is the same set —
"tool declaration → tool call → tool result." The framework must first be able to produce the <strong>tool declaration (JSON-Schema)</strong> required by the model, catch the
<strong>tool call</strong> returned by the model, and fill back the <strong>tool result</strong> after execution.</li>
<li class=""><strong>Then build a unified abstract encapsulation</strong>: Unify "declaration" as <code>ToolSpecification</code> / <code>ToolParameters</code>, unify "one call" as
<code>ToolExecutionRequest</code>, unify "result" as <code>ToolExecutionResult(Message)</code>, and unify "execution" as <code>ToolExecutor</code>.
After this step, local methods, HTTP, and MCP are all just <strong>declaration sources / execution implementations</strong> of this layer; and what ends up closest to the model is this
provider-neutral <strong>Function Tools declaration</strong>.</li>
</ol>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">本地 @Tool 方法 / HTTP / MCP   （都只是“声明来源”）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │  ToolSpecifications 扫描 / 各 Factory 构建</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">ToolSpecification + ToolParameters        ← 统一抽象层（provider-neutral）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │  Provider 映射（第 01 篇第四章）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Provider tools[]（OpenAI function.parameters / Anthropic input_schema）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ↑ 离模型最近的 Function Tools 层</span><br></div></code></pre></div></div>
<p><strong>Real implementation</strong>: The abstraction above maps one-to-one to concrete classes in the code:</p>
<table><thead><tr><th>Core Idea</th><th>Real Implementation (Code)</th></tr></thead><tbody><tr><td>Tool declaration</td><td><code>@Tool</code> / <code>@P</code> → <code>ToolSpecifications</code> → <code>ToolSpecification</code> / <code>ToolParameters</code></td></tr><tr><td>One call</td><td><code>ToolExecutionRequest(id, name, arguments)</code></td></tr><tr><td>Execution</td><td><code>ToolExecutor</code> / <code>DefaultToolExecutor</code> (local reflection)</td></tr><tr><td>Result fill-back</td><td><code>ToolExecutionResult</code> → <code>ToolExecutionResultMessage</code></td></tr><tr><td>Driving the loop</td><td><code>ToolService.chat(...)</code></td></tr></tbody></table>
<p><strong>Minimal core loop (real curl)</strong>: Put the abstraction above into a real round trip — standard Function Calling is essentially a "two-request" process:</p>
<p>① Request the model with a tool declaration:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> https://api.openai.com/v1/chat/completions </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Authorization: Bearer </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$OPENAI_API_KEY</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Content-Type: application/json"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-d</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'{</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "model": "gpt-4o-mini",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "messages": [ {"role":"user","content":"杭州今天天气怎么样？"} ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "tools": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      { "type":"function",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        "function": {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "name":"get_weather",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "description":"查询指定城市的当前天气",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "parameters": { "type":"object",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "properties": { "city": { "type":"string","description":"城市名" } },</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "required": ["city"] }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        } }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "tool_choice": "auto"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">  }'</span><br></div></code></pre></div></div>
<p>The model returns a tool call (<code>content</code> is <code>null</code>):</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"choices"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"index"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"finish_reason"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tool_calls"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token null keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"tool_calls"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"call_abc123"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"function"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token property">"function"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"arguments"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"{\"city\":\"杭州\"}"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"usage"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"prompt_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">62</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"completion_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">18</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"total_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">80</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>② After local execution, fill back the result with <code>role=tool</code> and request again:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> https://api.openai.com/v1/chat/completions </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Authorization: Bearer </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$OPENAI_API_KEY</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Content-Type: application/json"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-d</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'{</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "model": "gpt-4o-mini",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "messages": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      {"role":"user","content":"杭州今天天气怎么样？"},</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      {"role":"assistant","content":null,"tool_calls":[</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        {"id":"call_abc123","type":"function",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">         "function":{"name":"get_weather","arguments":"{\"city\":\"杭州\"}"}}]},</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      {"role":"tool","tool_call_id":"call_abc123","content":"杭州今天 22℃，天气晴。"}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "tools": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      { "type":"function",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        "function": { "name":"get_weather", "description":"查询指定城市的当前天气",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "parameters": { "type":"object",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "properties": { "city": { "type":"string","description":"城市名" } },</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "required": ["city"] } } }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "tool_choice": "auto"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">  }'</span><br></div></code></pre></div></div>
<p>The model gives the final answer:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"choices"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"index"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"finish_reason"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"stop"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"杭州今天 22℃，天气晴，适合出行。"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"usage"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"prompt_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">96</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"completion_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">12</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"total_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">108</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>In AgentForge, the round trip in ② is fully encapsulated: <code>tool_calls</code> → <code>ToolExecutionRequest</code> → <code>ToolExecutor</code> execution →
<code>ToolExecutionResultMessage</code> fill-back, automatically driven by <code>ToolService.chat()</code>, with no need for the application to hand-write this loop.</p>
<p><strong>Real usage scenarios</strong>: After this layer is implemented, the most common usages include —</p>
<ul>
<li class=""><strong>Real-time information</strong> such as weather, exchange rates, stock prices: the model decides "which tool to call and what arguments to pass," and the local method actually performs the lookup;</li>
<li class=""><strong>Business operations</strong> such as orders / accounts: querying orders, initiating refunds, where the <code>@Tool</code> method directly calls the existing Service;</li>
<li class=""><strong>Computation / data processing</strong>: calculators, unit conversion, SQL queries, turning natural language into structured calls;</li>
<li class=""><strong>Connecting to external systems</strong>: using <code>HttpToolExecutor</code> / <code>McpToolExecutor</code> to reuse the same <code>ToolService</code> loop.</li>
</ul>
<p>See Chapter 5, "Practical Example," for complete real request / return examples.</p>
<p>In summary, this chapter is divided into two parts: <strong>3.1 Standard Encapsulation</strong> (the protocol-layer contracts independent of vendor and execution location, which is also the source of extensibility) and
<strong>3.2 Default Implementation</strong> (a set of local Java tool implementations provided by AgentForge by default).</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-standard-encapsulation-function-calling-protocol-layer-abstraction-and-extensible-design">3.1 Standard Encapsulation: Function Calling Protocol-Layer Abstraction and Extensible Design<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#31-standard-encapsulation-function-calling-protocol-layer-abstraction-and-extensible-design" class="hash-link" aria-label="Direct link to 3.1 Standard Encapsulation: Function Calling Protocol-Layer Abstraction and Extensible Design" title="Direct link to 3.1 Standard Encapsulation: Function Calling Protocol-Layer Abstraction and Extensible Design" translate="no">​</a></h3>
<blockquote>
<p>Only covers the <strong>stable contracts independent of both vendor and execution location</strong>: how tool declarations are expressed, how execution is extended, and how calls and results are unified.
Local, HTTP, MCP, and custom executors are all built on top of this layer.</p>
</blockquote>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="311-toolspecification--toolparameters-unified-specification-for-tool-declarations">3.1.1 ToolSpecification / ToolParameters: Unified Specification for Tool Declarations<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#311-toolspecification--toolparameters-unified-specification-for-tool-declarations" class="hash-link" aria-label="Direct link to 3.1.1 ToolSpecification / ToolParameters: Unified Specification for Tool Declarations" title="Direct link to 3.1.1 ToolSpecification / ToolParameters: Unified Specification for Tool Declarations" translate="no">​</a></h4>
<p><code>ToolSpecification</code> is the immutable model of a tool declaration:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">ToolSpecification</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">ToolParameters</span><span class="token plain"> parameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(98, 114, 164)">// JSON-Schema 风格</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">Boolean</span><span class="token plain"> strict</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">              </span><span class="token comment" style="color:rgb(98, 114, 164)">// 是否要求 Provider 严格校验</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">Object</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p><code>ToolParameters</code> deliberately uses a <strong>pure <code>Map</code></strong> to carry JSON-Schema, keeping the api free of third-party dependencies:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">ToolParameters</span><span class="token plain"> parameters </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">ToolParameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">addProperty</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"city"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"string"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"城市名"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">   </span><span class="token comment" style="color:rgb(98, 114, 164)">// name / type / desc / required</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: OpenAI maps it to <code>tools[].function.parameters</code>, and Anthropic maps it to <code>tools[].input_schema</code>;
the same <code>ToolParameters</code> does not need to be written once per vendor.</p>
</blockquote>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="312-toolexecutor-a-replaceable-execution-extension-point">3.1.2 ToolExecutor: A Replaceable Execution Extension Point<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#312-toolexecutor-a-replaceable-execution-extension-point" class="hash-link" aria-label="Direct link to 3.1.2 ToolExecutor: A Replaceable Execution Extension Point" title="Direct link to 3.1.2 ToolExecutor: A Replaceable Execution Extension Point" translate="no">​</a></h4>
<p><code>ToolExecutor</code> is the contract for "how one tool call is executed," and it is also the key to the extensibility of the entire Tool layer:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@FunctionalInterface</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">interface</span><span class="token plain"> </span><span class="token class-name">ToolExecutor</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">execute</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ToolExecutionRequest</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">Object</span><span class="token plain"> memoryId</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token class-name">ToolExecutionResult</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">executeWithResult</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ToolExecutionRequest</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">Object</span><span class="token plain"> memoryId</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">String</span><span class="token plain"> text </span><span class="token operator">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">execute</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> memoryId</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token class-name">ToolExecutionResult</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">text</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Because it is a functional interface, the execution logic can come from anywhere:</p>
<table><thead><tr><th>Implementation</th><th>Description</th><th>Applicable Scenario</th></tr></thead><tbody><tr><td><code>DefaultToolExecutor</code> (default, local reflection)</td><td>Automatically generated from <code>@Tool</code> methods, reflectively invoked after binding arguments</td><td>The vast majority of ordinary Java methods</td></tr><tr><td>Custom <code>ToolExecutor</code> (Lambda)</td><td>Directly implements the execution logic, bypassing reflection</td><td>Cases requiring dynamic routing / non-annotated tools</td></tr><tr><td><code>HttpToolExecutor</code> (Agent layer)</td><td>Maps arguments into HTTP requests</td><td>Remote HTTP interfaces</td></tr><tr><td><code>McpToolExecutor</code> (Agent layer)</td><td>Forwards to an MCP Server</td><td>Remote MCP tools</td></tr></tbody></table>
<blockquote>
<p><strong>Key point</strong>: The upper layer only depends on the <code>ToolExecutor</code> contract, so adding a new kind of tool source (HTTP / MCP / custom) requires no changes to the declaration layer or the
protocol layer. See 3.2.3 for the default local implementation.</p>
</blockquote>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="313-toolexecutionrequest--toolexecutionresult-unified-call-and-result-model">3.1.3 ToolExecutionRequest / ToolExecutionResult: Unified Call and Result Model<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#313-toolexecutionrequest--toolexecutionresult-unified-call-and-result-model" class="hash-link" aria-label="Direct link to 3.1.3 ToolExecutionRequest / ToolExecutionResult: Unified Call and Result Model" title="Direct link to 3.1.3 ToolExecutionRequest / ToolExecutionResult: Unified Call and Result Model" translate="no">​</a></h4>
<p>The call returned by the model is unified as <code>ToolExecutionRequest</code>, and the execution output is unified as <code>ToolExecutionResult</code>:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">ToolExecutionRequest</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> arguments</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(98, 114, 164)">// 原始 JSON 字符串</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">ToolExecutionResult</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">boolean</span><span class="token plain"> isError</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">Object</span><span class="token plain"> result</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(98, 114, 164)">// 原始对象，不发给 LLM</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> text</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">     </span><span class="token comment" style="color:rgb(98, 114, 164)">// 发给 LLM 的文本</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token class-name">ToolExecutionResult</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">success</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">String</span><span class="token plain"> text</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token class-name">ToolExecutionResult</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">failure</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">String</span><span class="token plain"> text</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">Throwable</span><span class="token plain"> cause</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>One execution, together with its duration and raw object, is recorded as a <code>ToolExecution</code>:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">ToolExecution</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">ToolExecutionRequest</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">ToolExecutionResult</span><span class="token plain"> result</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">LocalDateTime</span><span class="token plain"> startTime</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">LocalDateTime</span><span class="token plain"> finishTime</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">Object</span><span class="token plain"> memoryId</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">resultText</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">boolean</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">hasFailed</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token class-name">Duration</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">duration</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: <code>result</code> (the raw object) and <code>text</code> (returned to the LLM) are separated, avoiding stuffing Java objects directly into protocol messages.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-default-implementation-tool-construction-encapsulation-for-local-java-methods">3.2 Default Implementation: Tool Construction Encapsulation for Local Java Methods<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#32-default-implementation-tool-construction-encapsulation-for-local-java-methods" class="hash-link" aria-label="Direct link to 3.2 Default Implementation: Tool Construction Encapsulation for Local Java Methods" title="Direct link to 3.2 Default Implementation: Tool Construction Encapsulation for Local Java Methods" translate="no">​</a></h3>
<blockquote>
<p>Covers a set of local tool implementations <strong>provided by AgentForge by default</strong>: annotate methods with <code>@Tool</code> / <code>@P</code>, automatically generate declarations, and execute via reflection.</p>
</blockquote>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="321-tool-and-p-declaring-java-methods-as-tools">3.2.1 @Tool and @P: Declaring Java Methods as Tools<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#321-tool-and-p-declaring-java-methods-as-tools" class="hash-link" aria-label="Direct link to 3.2.1 @Tool and @P: Declaring Java Methods as Tools" title="Direct link to 3.2.1 @Tool and @P: Declaring Java Methods as Tools" translate="no">​</a></h4>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@interface</span><span class="token plain"> </span><span class="token class-name">Tool</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">""</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">              </span><span class="token comment" style="color:rgb(98, 114, 164)">// 工具名，缺省用方法名</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">String</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">value</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">""</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">           </span><span class="token comment" style="color:rgb(98, 114, 164)">// 描述，多个字符串按换行拼接</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">ReturnBehavior</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">returnBehavior</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token class-name">ReturnBehavior</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">TO_LLM</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@interface</span><span class="token plain"> </span><span class="token class-name">P</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">""</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">              </span><span class="token comment" style="color:rgb(98, 114, 164)">// 参数名（javac 未开 -parameters 时尤其有用）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">value</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">""</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">             </span><span class="token comment" style="color:rgb(98, 114, 164)">// 参数描述（别名）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">""</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">       </span><span class="token comment" style="color:rgb(98, 114, 164)">// 参数描述（别名）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Usage:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">WeatherTools</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">value </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"查询指定城市的当前天气"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">getWeather</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@P</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">name </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"city"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> description </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"城市名"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> city</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"杭州今天 22℃，天气晴。"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Return value conventions (executed by <code>DefaultToolExecutor</code>):</p>
<ul>
<li class=""><code>String</code> -&gt; returned as-is;</li>
<li class=""><code>void</code> / <code>null</code> -&gt; the literal <code>"Success"</code>;</li>
<li class="">Other types -&gt; <code>Json.stringify(...)</code> into a JSON string.</li>
</ul>
<blockquote>
<p><strong>Note</strong>: <code>@P.value()</code> and <code>@P.description()</code> are aliases, and either one suffices; <code>@P.name()</code> is used to override the parameter name,
and it is recommended to always write it explicitly to avoid depending on the <code>-parameters</code> compile flag.</p>
</blockquote>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="322-toolspecifications-automatically-generating-json-schema-from-tool-methods">3.2.2 ToolSpecifications: Automatically Generating JSON-Schema from @Tool Methods<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#322-toolspecifications-automatically-generating-json-schema-from-tool-methods" class="hash-link" aria-label="Direct link to 3.2.2 ToolSpecifications: Automatically Generating JSON-Schema from @Tool Methods" title="Direct link to 3.2.2 ToolSpecifications: Automatically Generating JSON-Schema from @Tool Methods" translate="no">​</a></h4>
<p><code>ToolSpecifications</code> (core) is responsible for translating method signatures into specifications:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token class-name">ToolSpecification</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">toolSpecificationFrom</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Method</span><span class="token plain"> method</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">toolNameFrom</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Method</span><span class="token plain"> method</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token class-name">List</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ToolSpecification</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">toolSpecificationsFrom</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Object</span><span class="token plain"> objectWithTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token class-name">List</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ToolSpecification</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">toolSpecificationsFrom</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Class</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics operator">?</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> classWithTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">validateSpecifications</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">List</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ToolSpecification</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> toolSpecifications</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>Type mapping (Java -&gt; JSON-Schema):</p>
<table><thead><tr><th>Java Type</th><th>JSON Type</th></tr></thead><tbody><tr><td><code>String</code></td><td><code>string</code></td></tr><tr><td><code>int/Integer/long/Long/short/Short/byte/Byte</code></td><td><code>integer</code></td></tr><tr><td><code>double/Double/float/Float</code></td><td><code>number</code></td></tr><tr><td><code>boolean/Boolean</code></td><td><code>boolean</code></td></tr><tr><td>enum</td><td><code>string</code></td></tr><tr><td>array / <code>List</code> / <code>Set</code></td><td><code>array</code></td></tr><tr><td><code>Map</code></td><td><code>object</code></td></tr></tbody></table>
<p>During scanning, the class hierarchy is traversed (superclass methods are also included), and deduplication is done by "method signature."</p>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="323-defaulttoolexecutor-reflective-execution-and-argument-binding">3.2.3 DefaultToolExecutor: Reflective Execution and Argument Binding<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#323-defaulttoolexecutor-reflective-execution-and-argument-binding" class="hash-link" aria-label="Direct link to 3.2.3 DefaultToolExecutor: Reflective Execution and Argument Binding" title="Direct link to 3.2.3 DefaultToolExecutor: Reflective Execution and Argument Binding" translate="no">​</a></h4>
<p><code>DefaultToolExecutor</code> is the default local implementation, whose core is "binding the <code>arguments</code> JSON to method parameters." The key steps:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">Object</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> arguments </span><span class="token operator">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">prepareArguments</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(98, 114, 164)">// arguments JSON -&gt; Map -&gt; 按参数名取值</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">method</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">setAccessible</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> method</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">invoke</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">object</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> arguments</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>Argument binding performs <strong>lightweight type conversion</strong>: <code>String</code> / numbers (including <code>BigDecimal</code> compatibility) / <code>boolean</code> / enum / <code>Map</code>;
if a required argument is missing, a <code>ToolArgumentsException</code> is thrown; if the method itself throws an exception, it is wrapped as a <code>ToolExecutionException</code>.</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">text</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Object</span><span class="token plain"> result</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">result </span><span class="token operator">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Success"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">result </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">instanceof</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">String</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> result</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token class-name">Json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">stringify</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">result</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">          </span><span class="token comment" style="color:rgb(98, 114, 164)">// 非字符串统一序列化为 JSON</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Note</strong>: <code>DefaultToolExecutor</code> unwraps <code>InvocationTargetException.getCause()</code> — if the method throws a
<code>ToolArgumentsException</code>, it is rethrown as-is; otherwise it is uniformly wrapped as a <code>ToolExecutionException</code>, avoiding exception disguise.</p>
</blockquote>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="324-toolexecutionrequestutil-arguments-json---map">3.2.4 ToolExecutionRequestUtil: arguments JSON -&gt; Map<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#324-toolexecutionrequestutil-arguments-json---map" class="hash-link" aria-label="Direct link to 3.2.4 ToolExecutionRequestUtil: arguments JSON -> Map" title="Direct link to 3.2.4 ToolExecutionRequestUtil: arguments JSON -> Map" translate="no">​</a></h4>
<p>The <code>arguments</code> given by the model is a <strong>raw JSON string</strong> and must be parsed before execution:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">Object</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">argumentsAsMap</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ToolExecutionRequest</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)">// 空 / "{}" -&gt; 空 Map</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)">// 解析失败 -&gt; ToolArgumentsException</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)">// 解析结果不是 JSON 对象 -&gt; ToolArgumentsException</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Drawback note</strong>: The model occasionally produces invalid JSON or <code>arguments</code> whose types do not match. Such errors are explicitly classified as
<code>ToolArgumentsException</code> and handed to a dedicated handler, rather than letting them become hard-to-locate runtime exceptions.</p>
</blockquote>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="325-returnbehavior-result-return-behavior">3.2.5 ReturnBehavior: Result Return Behavior<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#325-returnbehavior-result-return-behavior" class="hash-link" aria-label="Direct link to 3.2.5 ReturnBehavior: Result Return Behavior" title="Direct link to 3.2.5 ReturnBehavior: Result Return Behavior" translate="no">​</a></h4>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">enum</span><span class="token plain"> </span><span class="token class-name">ReturnBehavior</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token constant" style="color:rgb(189, 147, 249)">TO_LLM</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain">               </span><span class="token comment" style="color:rgb(98, 114, 164)">// 默认：结果回给 LLM，继续下一轮</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token constant" style="color:rgb(189, 147, 249)">IMMEDIATE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain">            </span><span class="token comment" style="color:rgb(98, 114, 164)">// 执行后立即返回给调用方，终止循环</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token constant" style="color:rgb(189, 147, 249)">IMMEDIATE_IF_LAST</span><span class="token plain">     </span><span class="token comment" style="color:rgb(98, 114, 164)">// 仅当它是本轮最后一个工具调用时立即返回</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Immediate-return determination rules (any tool error forces another round):</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">[]                                       -&gt; 继续（无工具调用）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[TO_LLM, ...] 含任一 TO_LLM               -&gt; 继续</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[IMMEDIATE] | [IMMEDIATE, IMMEDIATE]     -&gt; 立即返回</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[IMMEDIATE_IF_LAST]                      -&gt; 立即返回</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[.., IMMEDIATE_IF_LAST]（最后一个）       -&gt; 立即返回</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-toolservice-the-reasoning-execution-loop">4. ToolService: The Reasoning-Execution Loop<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#4-toolservice-the-reasoning-execution-loop" class="hash-link" aria-label="Direct link to 4. ToolService: The Reasoning-Execution Loop" title="Direct link to 4. ToolService: The Reasoning-Execution Loop" translate="no">​</a></h2>
<p><code>ToolService</code> (core) is the execution engine that strings all the contracts above together.</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="41-registering-tools">4.1 Registering Tools<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#41-registering-tools" class="hash-link" aria-label="Direct link to 4.1 Registering Tools" title="Direct link to 4.1 Registering Tools" translate="no">​</a></h3>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">ToolService</span><span class="token plain"> toolService </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">ToolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// 1) 扫描对象内的全部 @Tool 方法</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Arrays</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">asList</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">WeatherTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// 2) 注册单个工具对象</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">WeatherTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// 3) 指定方法名</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">WeatherTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"getWeather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// 4) 直接注册 specification + executor</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">specification</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> request </span><span class="token operator">-&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"..."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// 5) 批量注册 Map&lt;ToolSpecification, ToolExecutor&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">map</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>Duplicate tool names throw an <code>IllegalArgumentException</code>, ensuring the registry is unique.</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="42-chat-main-loop">4.2 chat() Main Loop<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#42-chat-main-loop" class="hash-link" aria-label="Direct link to 4.2 chat() Main Loop" title="Direct link to 4.2 chat() Main Loop" translate="no">​</a></h3>
<p><strong>Guiding question</strong>: The model may request tool calls across multiple consecutive rounds; how can the loop be both automatic and safe?</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token class-name">ToolChatResult</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">chat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">ChatModel</span><span class="token plain"> model</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">ChatRequestParameters</span><span class="token plain"> parameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">List</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ChatMessage</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> messages</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">1. 携带已注册 tools 调用 ChatModel</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">2. 若 aiMessage.hasToolExecutionRequests()：</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     a. 逐个执行工具（找不到 executor -&gt; 幻觉工具策略，默认抛异常）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     b. 把每个 ToolExecutionResult 转成 ToolExecutionResultMessage（id 关联）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     c. 追加到消息列表</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     d. 若 ReturnBehavior 要求立即返回 -&gt; 停止</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     e. 否则用新消息继续下一轮</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">3. 若无工具调用 -&gt; 返回最终 ChatResponse + 全部 ToolExecution</span><br></div></code></pre></div></div>
<ul>
<li class="">The default <code>maxToolCallingRoundTrips = 100</code>; exceeding it throws an <code>IllegalStateException</code>, preventing the model from falling into an infinite loop;</li>
<li class="">Results are returned via <code>ToolChatResult</code>: <code>finalResponse()</code> / <code>toolExecutions()</code> / <code>intermediateResponses()</code>.</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="43-parameter-merging-automatically-injecting-tools-into-the-request">4.3 Parameter Merging: Automatically Injecting Tools into the Request<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#43-parameter-merging-automatically-injecting-tools-into-the-request" class="hash-link" aria-label="Direct link to 4.3 Parameter Merging: Automatically Injecting Tools into the Request" title="Direct link to 4.3 Parameter Merging: Automatically Injecting Tools into the Request" translate="no">​</a></h3>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token class-name">ChatRequestParameters</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">requestParameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ChatRequestParameters</span><span class="token plain"> userParameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">DefaultChatRequestParameters</span><span class="token plain"> toolParameters </span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token class-name">DefaultChatRequestParameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">ArrayList</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ToolSpecification</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">toolSpecifications</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)">// 用户参数覆盖非工具字段；已注册工具始终包含</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token class-name">DefaultChatRequestParameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">merge</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">toolParameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> userParameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: The caller does not need to manually stuff <code>tools()</code> into <code>ChatRequestParameters</code> every time; <code>ToolService</code> automatically merges registered tools in, while preserving the user's overrides of other parameters.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="44-error-handling">4.4 Error Handling<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#44-error-handling" class="hash-link" aria-label="Direct link to 4.4 Error Handling" title="Direct link to 4.4 Error Handling" translate="no">​</a></h3>
<table><thead><tr><th>Exception</th><th>Trigger Point</th><th>Default Handling</th></tr></thead><tbody><tr><td><code>ToolArgumentsException</code></td><td>Argument JSON cannot be parsed / type mismatch / missing required argument</td><td>Throw exception (<code>RETHROW</code>)</td></tr><tr><td><code>ToolExecutionException</code></td><td>Tool method execution fails</td><td>Return to the LLM as <code>ToolErrorHandlerResult.text(...)</code></td></tr></tbody></table>
<p>Both kinds of errors can be customized:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">argumentsErrorHandler</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">error</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> context</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">-&gt;</span><span class="token plain"> </span><span class="token class-name">ToolErrorHandlerResult</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">text</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">error</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">executionErrorHandler</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">error</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> context</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">-&gt;</span><span class="token plain"> </span><span class="token class-name">ToolErrorHandlerResult</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">text</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"工具执行失败，请重试"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>A handler has two ways to return:</p>
<ul>
<li class="">Return <code>ToolErrorHandlerResult.text(msg)</code> -&gt; the error message is returned to the LLM as the tool result, and the model can <strong>self-correct and retry</strong> after seeing it;</li>
<li class="">Throw an exception directly -&gt; terminate the entire tool loop and propagate it up to the caller.</li>
</ul>
<p>In addition, there is a "hallucinated tool" strategy: when the model requests an unregistered tool, by default <code>THROW_ON_HALLUCINATED</code> directly throws an exception,
which can be replaced by any <code>Function&lt;ToolExecutionRequest, ToolExecutionResultMessage&gt;</code>.</p>
<blockquote>
<p><strong>Note</strong>: The default of returning execution failures to the LLM (<code>DEFAULT_TOOL_EXECUTION_ERROR_HANDLER</code>) is to give the model a chance to correct
its arguments or switch tools; but invalid arguments are thrown by default directly, avoiding treating obvious errors as normal results and continuing.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="45-relationship-with-provider-function-calling">4.5 Relationship with Provider Function Calling<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#45-relationship-with-provider-function-calling" class="hash-link" aria-label="Direct link to 4.5 Relationship with Provider Function Calling" title="Direct link to 4.5 Relationship with Provider Function Calling" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">ToolService.chat()                     &lt;- 上层 Agent 入口（本篇）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">ChatModel / ChatRequest / ChatResponse &lt;- 第 01 篇标准协议</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">OpenAI / Anthropic Adapter             &lt;- wire 映射（tool_calls / tool_use）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">AiMessage.toolExecutionRequests()      &lt;- 模型给出的工具调用</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">ToolService 执行 -&gt; ToolExecutionResultMessage 回填</span><br></div></code></pre></div></div>
<p>Two layers are decoupled: the Provider is responsible for "parsing the protocol into a <code>ToolExecutionRequest</code>," and <code>ToolService</code> is responsible for "executing and filling back."</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-practical-example-real-curl-and-returned-results">5. Practical Example: Real curl and Returned Results<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#5-practical-example-real-curl-and-returned-results" class="hash-link" aria-label="Direct link to 5. Practical Example: Real curl and Returned Results" title="Direct link to 5. Practical Example: Real curl and Returned Results" translate="no">​</a></h2>
<p>This section walks through the complete chain using OpenAI Function Calling: define a tool → get <code>tool_calls</code> in the first round → execute locally →
fill back <code>tool_result</code> in the second round → get the final answer, and finally demonstrates doing it all at once with <code>ToolService</code>.</p>
<blockquote>
<p>Note: The return values below are the <strong>real protocol structure</strong> (<code>id</code>, timestamps, and token counts will vary with actual calls).</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="51-define-and-register-a-tool">5.1 Define and Register a Tool<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#51-define-and-register-a-tool" class="hash-link" aria-label="Direct link to 5.1 Define and Register a Tool" title="Direct link to 5.1 Define and Register a Tool" translate="no">​</a></h3>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">WeatherTools</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">value </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"查询指定城市的当前天气"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">getWeather</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@P</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">name </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"city"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> description </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"城市名"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> city</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"杭州今天 22℃，天气晴。"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ToolService</span><span class="token plain"> toolService </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">ToolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">WeatherTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>The tool declaration generated by <code>ToolSpecifications</code>, taking OpenAI's <code>tools[]</code> as an example, after serialization is equivalent to:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"function"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"function"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"getWeather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"description"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"查询指定城市的当前天气"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"parameters"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"object"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"properties"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"city"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"string"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"description"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"城市名"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"required"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"city"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="52-first-round-the-model-returns-tool_calls">5.2 First Round: The Model Returns tool_calls<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#52-first-round-the-model-returns-tool_calls" class="hash-link" aria-label="Direct link to 5.2 First Round: The Model Returns tool_calls" title="Direct link to 5.2 First Round: The Model Returns tool_calls" translate="no">​</a></h3>
<p>curl:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> https://api.openai.com/v1/chat/completions </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Authorization: Bearer </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$OPENAI_API_KEY</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Content-Type: application/json"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-d</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'{</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "model": "gpt-4o-mini",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "messages": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      {"role": "user", "content": "杭州今天天气怎么样？"}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "tools": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        "type": "function",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        "function": {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "name": "get_weather",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "description": "查询指定城市的当前天气",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "parameters": {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "type": "object",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "properties": { "city": { "type": "string", "description": "城市名" } },</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "required": ["city"]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "tool_choice": "auto"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">  }'</span><br></div></code></pre></div></div>
<p>Return (<code>content</code> is <code>null</code>, the model requests to call a tool):</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"chatcmpl-9xYzAbC456"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"object"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"chat.completion"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"created"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">1726200100</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"model"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"gpt-4o-mini"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"choices"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"index"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token null keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"tool_calls"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"call_abc123"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"function"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token property">"function"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"arguments"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"{\"city\":\"杭州\"}"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"finish_reason"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tool_calls"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"usage"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"prompt_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">62</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"completion_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">18</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"total_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">80</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>What the AgentForge side receives is an <code>AiMessage</code>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">AiMessage.text() == null</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">AiMessage.hasToolExecutionRequests() == true</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">AiMessage.toolExecutionRequests() == [ToolExecutionRequest(id=call_abc123, name=get_weather, arguments={"city":"杭州"})]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">ChatResponse.finishReason() == TOOL_EXECUTION</span><br></div></code></pre></div></div>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="53-execute-the-tool-locally">5.3 Execute the Tool Locally<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#53-execute-the-tool-locally" class="hash-link" aria-label="Direct link to 5.3 Execute the Tool Locally" title="Direct link to 5.3 Execute the Tool Locally" translate="no">​</a></h3>
<p>Internally, <code>ToolService</code> does three things: find the <code>ToolExecutor</code> by tool name → parse <code>arguments</code> → execute reflectively.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">request.name      = "get_weather"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">request.arguments = "{\"city\":\"杭州\"}"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │  ToolExecutionRequestUtil.argumentsAsMap()</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Map{ city = "杭州" }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │  DefaultToolExecutor.prepareArguments() + coerce()</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">getWeather("杭州")</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">ToolExecutionResult{ text = "杭州今天 22℃，天气晴。", isError = false }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">ToolExecutionResultMessage(id="call_abc123", toolName="get_weather", text="杭州今天 22℃，天气晴。")</span><br></div></code></pre></div></div>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="54-second-round-fill-back-the-tool-result">5.4 Second Round: Fill Back the tool result<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#54-second-round-fill-back-the-tool-result" class="hash-link" aria-label="Direct link to 5.4 Second Round: Fill Back the tool result" title="Direct link to 5.4 Second Round: Fill Back the tool result" translate="no">​</a></h3>
<p>curl (sending the assistant's <code>tool_calls</code> together with the <code>role=tool</code> result):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> https://api.openai.com/v1/chat/completions </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Authorization: Bearer </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$OPENAI_API_KEY</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Content-Type: application/json"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-d</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'{</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "model": "gpt-4o-mini",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "messages": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      {"role": "user", "content": "杭州今天天气怎么样？"},</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      {"role": "assistant", "content": null, "tool_calls": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        {"id": "call_abc123", "type": "function",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">         "function": {"name": "get_weather", "arguments": "{\"city\":\"杭州\"}"}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      ]},</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      {"role": "tool", "tool_call_id": "call_abc123", "content": "杭州今天 22℃，天气晴。"}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "tools": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        "type": "function",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        "function": {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "name": "get_weather",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "description": "查询指定城市的当前天气",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "parameters": {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "type": "object",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "properties": { "city": { "type": "string", "description": "城市名" } },</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "required": ["city"]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "tool_choice": "auto"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">  }'</span><br></div></code></pre></div></div>
<p>Return (the model gives the final answer based on the tool result):</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"chatcmpl-9xYzAbC789"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"object"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"chat.completion"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"created"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">1726200105</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"model"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"gpt-4o-mini"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"choices"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"index"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"杭州今天 22℃，天气晴，适合出行。"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"finish_reason"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"stop"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"usage"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"prompt_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">96</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"completion_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">12</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"total_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">108</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: The <code>tool_call_id</code> of <code>role=tool</code> must match the <code>tool_calls[].id</code> of the assistant (here
<code>call_abc123</code>); AgentForge uses <code>ToolExecutionResultMessage.id</code> to carry this association.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="55-completing-multiple-rounds-at-once-with-toolservice">5.5 Completing Multiple Rounds at Once with ToolService<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#55-completing-multiple-rounds-at-once-with-toolservice" class="hash-link" aria-label="Direct link to 5.5 Completing Multiple Rounds at Once with ToolService" title="Direct link to 5.5 Completing Multiple Rounds at Once with ToolService" translate="no">​</a></h3>
<p>The manual orchestration of the above two rounds is done in one line with <code>ToolService.chat()</code>:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">ToolService</span><span class="token plain"> toolService </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">ToolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">WeatherTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">List</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ChatMessage</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> messages </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">ArrayList</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ChatMessage</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">messages</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">add</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">UserMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"杭州今天天气怎么样？"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ToolChatResult</span><span class="token plain"> result </span><span class="token operator">=</span><span class="token plain"> toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">chat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">model</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">/* parameters = */</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> messages</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">result</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">finalResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">aiMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">text</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(98, 114, 164)">// 杭州今天 22℃，天气晴，适合出行。</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">result</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">toolExecutions</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">size</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">              </span><span class="token comment" style="color:rgb(98, 114, 164)">// 1（本次调用了几次工具）</span><br></div></code></pre></div></div>
<p>From <code>result.toolExecutions()</code> you can obtain the <code>request</code> / <code>result</code> / start and end times of each execution, which is convenient for logging and observability.</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="56-anthropic-tool_use-comparison">5.6 Anthropic tool_use Comparison<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#56-anthropic-tool_use-comparison" class="hash-link" aria-label="Direct link to 5.6 Anthropic tool_use Comparison" title="Direct link to 5.6 Anthropic tool_use Comparison" translate="no">​</a></h3>
<p>The same <code>ToolService</code> code needs no changes; when switching the underlying provider to Anthropic, the wire form differs but the semantics are consistent:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">OpenAI                                      Anthropic</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  assistant.tool_calls[] {id,name,arguments}  assistant.content[] {type:tool_use, id,name,input}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  role=tool, tool_call_id=...                 role=user, content=[{type:tool_result, tool_use_id=...}]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │                                            │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        └──────────────► ToolExecutionRequest ◄──────┘</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                              │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                     ToolService 执行</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                              │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                     ToolExecutionResultMessage</span><br></div></code></pre></div></div>
<p>Anthropic's second-round fill-back message looks like:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"user"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tool_result"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"tool_use_id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"toolu_1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"杭州今天 22℃，天气晴。"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>For the real curl and streaming <code>input_json_delta</code> aggregation, see
[Anthropic Protocol 02, AgentForge Anthropic Integration Core Practice.md](/blog/01、model模型协议层/chatmodel/anthropic/Anthropic协议02、AgentForge 适配Anthropic接入核心实践).</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="57-one-click-real-verification-script">5.7 One-Click Real Verification Script<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#57-one-click-real-verification-script" class="hash-link" aria-label="Direct link to 5.7 One-Click Real Verification Script" title="Direct link to 5.7 One-Click Real Verification Script" translate="no">​</a></h3>
<p>The verification scripts in the repository already cover tool calling:</p>
<ul>
<li class="">openai/verify-openai-chat.sh</li>
<li class="">anthropic/verify-anthropic-chat.sh</li>
</ul>
<p>Taking OpenAI as an example:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token builtin class-name" style="color:rgb(189, 147, 249)">export</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">OPENAI_API_KEY</span><span class="token operator">=</span><span class="token plain">sk-</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token plain">.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">bash</span><span class="token plain"> openai/verify-openai-chat.sh</span><br></div></code></pre></div></div>
<p>The script executes, in order, "non-streaming request -&gt; jq parses <code>tool_calls</code> -&gt; streaming request observing <code>data:</code> frames."</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-verification-tests">6. Verification Tests<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#6-verification-tests" class="hash-link" aria-label="Direct link to 6. Verification Tests" title="Direct link to 6. Verification Tests" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="61-test-matrix">6.1 Test Matrix<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#61-test-matrix" class="hash-link" aria-label="Direct link to 6.1 Test Matrix" title="Direct link to 6.1 Test Matrix" translate="no">​</a></h3>
<table><thead><tr><th>Module</th><th>Test Class</th><th>Coverage</th></tr></thead><tbody><tr><td><code>agentforge-model-core</code></td><td><code>DefaultToolExecutorTest</code></td><td>Argument binding and type conversion, <code>String</code>/<code>void</code>/object return value to text, missing argument throws <code>ToolArgumentsException</code>, method exception wrapped as <code>ToolExecutionException</code></td></tr><tr><td><code>agentforge-model-core</code></td><td><code>ToolServiceTest</code></td><td>Tool registration deduplication, reasoning-execution loop, <code>ReturnBehavior</code> immediate return, hallucinated-tool strategy, two kinds of error handlers, <code>@Tool</code> method -&gt; <code>ToolSpecification</code> / type mapping</td></tr><tr><td><code>agentforge-model-openai</code></td><td><code>OpenAiFunctionCallTest</code></td><td><code>tools</code>/<code>tool_choice</code> serialization, <code>tool_calls</code> parsing, tool result flow-back</td></tr><tr><td><code>agentforge-model-anthropic</code></td><td><code>AnthropicToolUseTest</code></td><td><code>tool_use</code> parsing, <code>tool_result</code> flow-back, <code>input_schema</code> serialization</td></tr></tbody></table>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="62-key-assertions">6.2 Key Assertions<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#62-key-assertions" class="hash-link" aria-label="Direct link to 6.2 Key Assertions" title="Direct link to 6.2 Key Assertions" translate="no">​</a></h3>
<ul>
<li class=""><code>argumentsAsMap</code>: empty string / <code>"{}"</code> -&gt; empty Map; invalid JSON -&gt; <code>ToolArgumentsException</code>;</li>
<li class=""><code>DefaultToolExecutor</code>: <code>null</code> return value -&gt; <code>"Success"</code>; object return value -&gt; JSON string;</li>
<li class=""><code>ToolService</code>: duplicate tool name registration -&gt; <code>IllegalArgumentException</code>;</li>
<li class="">Loop: returns the final response when the model no longer requests tools; exceeding <code>maxToolCallingRoundTrips</code> -&gt; <code>IllegalStateException</code>;</li>
<li class="">Errors: execution failures are returned to the LLM by default; argument errors are thrown by default.</li>
</ul>
<blockquote>
<p><strong>Key point</strong>: All tests are completed using a Fake <code>HttpTransport</code> and a "fake model," without depending on a real API Key; tool execution itself is pure local logic,
and can be fully asserted offline.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-boundaries-compatibility-and-evolution">7. Boundaries, Compatibility and Evolution<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#7-boundaries-compatibility-and-evolution" class="hash-link" aria-label="Direct link to 7. Boundaries, Compatibility and Evolution" title="Direct link to 7. Boundaries, Compatibility and Evolution" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="71-capabilities-already-implemented">7.1 Capabilities Already Implemented<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#71-capabilities-already-implemented" class="hash-link" aria-label="Direct link to 7.1 Capabilities Already Implemented" title="Direct link to 7.1 Capabilities Already Implemented" translate="no">​</a></h3>
<ul>
<li class=""><code>@Tool</code> / <code>@P</code> declarative tool definitions;</li>
<li class=""><code>ToolSpecifications</code> automatically generates <code>ToolSpecification</code> / <code>ToolParameters</code>;</li>
<li class=""><code>DefaultToolExecutor</code> reflective execution + lightweight type conversion;</li>
<li class="">Custom <code>ToolExecutor</code> (Lambda);</li>
<li class=""><code>ToolService</code> reasoning-execution loop, automatic parameter injection, maximum-rounds protection;</li>
<li class=""><code>ReturnBehavior</code> (<code>TO_LLM</code> / <code>IMMEDIATE</code> / <code>IMMEDIATE_IF_LAST</code>);</li>
<li class="">Two kinds of error handlers, <code>ToolErrorHandlerResult</code> error-return self-healing, hallucinated-tool strategy;</li>
<li class=""><code>ToolExecution</code> / <code>ToolExecutionResult</code> execution result value objects;</li>
<li class="">Integration with OpenAI / Anthropic tool protocols (for wire mapping, see Chapter 4 of Article 01).</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="72-capabilities-still-to-be-completed">7.2 Capabilities Still To Be Completed<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#72-capabilities-still-to-be-completed" class="hash-link" aria-label="Direct link to 7.2 Capabilities Still To Be Completed" title="Direct link to 7.2 Capabilities Still To Be Completed" translate="no">​</a></h3>
<ul>
<li class="">Richer argument type binding (nested POJOs, strongly-typed deserialization of generic collections);</li>
<li class="">Structured Output / JSON-Schema runtime validation;</li>
<li class="">Concurrent / asynchronous tool execution (the current loop is sequential);</li>
<li class="">Tool-level timeout and cancellation (Cancellation);</li>
<li class="">Standardization of tool-call auditing / metrics / trace;</li>
<li class=""><code>onPartialToolCall(...)</code> incremental callback in streaming scenarios (currently only the final response exposes the complete tool call).</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="73-trade-offs-relative-to-langchain4j">7.3 Trade-offs Relative to LangChain4j<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#73-trade-offs-relative-to-langchain4j" class="hash-link" aria-label="Direct link to 7.3 Trade-offs Relative to LangChain4j" title="Direct link to 7.3 Trade-offs Relative to LangChain4j" translate="no">​</a></h3>
<ul>
<li class="">Borrows its simple style of <code>@Tool</code> / <code>@P</code> / <code>ToolSpecification</code> / <code>ToolExecutor</code> / <code>ToolService</code>;</li>
<li class="">But <strong>does not introduce AI-Service, reflection chains, or compensation/async mechanisms</strong>, keeping only a self-contained minimal execution engine;</li>
<li class=""><code>ToolParameters</code> uses a pure <code>Map</code>, without introducing a JSON library dependency, keeping the api lightweight.</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="74-compatibility-principles">7.4 Compatibility Principles<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#74-compatibility-principles" class="hash-link" aria-label="Direct link to 7.4 Compatibility Principles" title="Direct link to 7.4 Compatibility Principles" translate="no">​</a></h3>
<ol>
<li class="">Tool contracts prioritize adding default methods or new types, avoiding breaking existing implementations;</li>
<li class="">Vendor wire details stay in the Provider, and the <code>tool</code> package remains Provider-neutral;</li>
<li class="">Execution result <code>result</code> (the raw object) and <code>text</code> (returned to the LLM) are separated;</li>
<li class="">The public API remains compilable on Java 8, with a default recommendation to run on JDK 17;</li>
<li class="">Error handling is explicitly configurable, with the default "execution failure returned to the LLM, invalid arguments throw an exception."</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-summary">8. Summary<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#8-summary" class="hash-link" aria-label="Direct link to 8. Summary" title="Direct link to 8. Summary" translate="no">​</a></h2>
<p>Back to the question at the beginning: <strong>After the model says "I want to call a tool," who executes it?</strong></p>
<p>AgentForge's answer is: split "declaration → specification → execution → loop → error" into a set of stable contracts, then string them together with <code>ToolService</code> —</p>
<ul>
<li class=""><strong>Declaration</strong>: <code>@Tool</code> / <code>@P</code> turn Java methods into tools;</li>
<li class=""><strong>Specification</strong>: <code>ToolSpecifications</code> generates vendor-neutral <code>ToolSpecification</code> / <code>ToolParameters</code>;</li>
<li class=""><strong>Execution</strong>: <code>ToolExecutor</code> + <code>DefaultToolExecutor</code> complete argument binding and reflective invocation;</li>
<li class=""><strong>Loop</strong>: <code>ToolService.chat()</code> automatically drives multi-round tool calls, using <code>ReturnBehavior</code> and maximum rounds to keep it controllable;</li>
<li class=""><strong>Error</strong>: distinguishes argument errors from execution errors, which can be returned to the LLM for self-healing or terminated directly.</li>
</ul>
<p>With this layer, the upper-layer Agent only needs to program against <code>ToolExecutionRequest</code> / <code>ToolExecutor</code> to reuse the same set of tool capabilities on top of OpenAI, Anthropic, and even any future protocol.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8202%E3%80%81Function%20Calling%20%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8%E8%BD%AC%E6%8D%A2%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0%EF%BC%9ATool%20%E5%A5%91%E7%BA%A6%20+%20ToolService%20%E6%89%A7%E8%A1%8C%E5%BE%AA%E7%8E%AF#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<p>[1]. <a href="https://developers.openai.com/api/docs/guides/function-calling" target="_blank" rel="noopener noreferrer" class="">OpenAI - Function calling guide</a></p>
<p>[2]. <a href="https://openai.com/index/function-calling-and-other-api-updates/" target="_blank" rel="noopener noreferrer" class="">OpenAI - Function calling and other API updates</a></p>
<p>[3]. <a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview" target="_blank" rel="noopener noreferrer" class="">Anthropic - Tool use with Claude</a></p>
<p>[4]. <a href="https://claude.com/blog/tool-use-ga" target="_blank" rel="noopener noreferrer" class="">Anthropic - Claude can now use tools</a></p>
<p>[5]. <a href="https://arxiv.org/abs/2409.18807" target="_blank" rel="noopener noreferrer" class="">arXiv - LLM With Tools: A Survey (2409.18807)</a></p>
<p>[6]. <a href="https://gorilla.cs.berkeley.edu/leaderboard.html" target="_blank" rel="noopener noreferrer" class="">Berkeley Function Calling Leaderboard (BFCL)</a></p>
<p>[7]. <a href="https://ai.google.dev/gemini-api/docs/function-calling" target="_blank" rel="noopener noreferrer" class="">Google - Gemini API Function calling</a></p>
<p>[8]. <a href="https://api-docs.deepseek.com/guides/tool_calls/" target="_blank" rel="noopener noreferrer" class="">DeepSeek - Tool Calls</a></p>
<p>[9]. <a href="https://docs.vllm.ai/en/stable/features/tool_calling/" target="_blank" rel="noopener noreferrer" class="">vLLM - Tool Calling</a></p>
<p>[10]. <a href="https://docs.ollama.com/capabilities/tool-calling" target="_blank" rel="noopener noreferrer" class="">Ollama - Tool calling</a></p>
<p>[11]. <a href="https://github.com/langchain4j/langchain4j" target="_blank" rel="noopener noreferrer" class="">LangChain4j - Tools (Function Calling)</a></p>
<p>[12]. <a href="https://json-schema.org/" target="_blank" rel="noopener noreferrer" class="">JSON Schema</a></p>
<p>Compiled by: Changlu Created: 2026.10.6 Updated: 2026.10.6</p>]]></content:encoded>
            <category>AgentForge</category>
            <category>Model Protocol Layer</category>
            <category>ChatModel</category>
            <category>Function Calling</category>
        </item>
        <item>
            <title><![CDATA[AgentForge Model Protocol Layer ChatModel Principles 01, ChatModel Core Protocol Layer Design: Unified API Contract Encapsulation and Multi-Protocol Extension Implementation]]></title>
            <link>https://agentforge-tech.github.io/AgentForge/en/blog/01、model模型协议层/chatmodel/统一ChatModel协议层01、ChatModel 核心协议层设计：统一 API 契约封装与多协议扩展实现</link>
            <guid>https://agentforge-tech.github.io/AgentForge/en/blog/01、model模型协议层/chatmodel/统一ChatModel协议层01、ChatModel 核心协议层设计：统一 API 契约封装与多协议扩展实现</guid>
            <pubDate>Thu, 01 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Applicable version: release_1.x]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>Applicable version: release_1.x<br>
<!-- -->Applicable module: <code>agentforge-model</code> (core contract: <code>agentforge-model-api</code>)<br>
<!-- -->Package prefix: <code>cloud.changlu.agentforge.model</code><br>
<!-- -->Maintainer: Changlu</p>
</blockquote>
<!-- -->
<p>Series positioning: This article belongs to "AgentForge Core Module Design Principles Article Series / 01, model Model Protocol Layer / chatmodel", and is the 01st article of the ChatModel category principles course. The entire text only covers two things:</p>
<ol>
<li class="">How <code>agentforge-model-api</code> uses a set of <strong>Provider-neutral interfaces</strong> to encapsulate the unified input/output of ChatModel;</li>
<li class="">How <strong>different protocol models such as OpenAI and Anthropic are extended and implemented on top of this standard ChatModel protocol</strong>.</li>
</ol>
<p>The Provider's underlying wire protocol (field names, Header, SSE, finish reason) is placed separately in the
<code>openai/</code> and <code>anthropic/</code> directories, to avoid mixing Core design with vendor protocols.</p>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="documentation-navigation">Documentation Navigation<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#documentation-navigation" class="hash-link" aria-label="Direct link to Documentation Navigation" title="Direct link to Documentation Navigation" translate="no">​</a></h2>
<table><thead><tr><th>Document</th><th>Description</th></tr></thead><tbody><tr><td>Current article (ChatModel Principles 01)</td><td><code>agentforge-model-api</code> interface encapsulation, standard ChatModel protocol, multi-protocol extension design</td></tr><tr><td>[ChatModel Principles 02, Function Calling Tool Invocation Conversion and Execution Extension Implementation: Tool Contract + ToolService Execution Loop.md](/blog/01、model模型协议层/chatmodel/统一ChatModel协议层02、Function Calling 工具调用转换与执行扩展实现：Tool 契约 + ToolService 执行循环)</td><td>Article 02: <code>@Tool</code> declaration, tool specification, reflective execution and <code>ToolService</code> execution loop</td></tr><tr><td><a class="" href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/openai/OpenAI%E5%8D%8F%E8%AE%AE01%E3%80%81OpenAI%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3">openai/OpenAI Protocol 01, A Quick Understanding of the OpenAI Underlying Protocol.md</a></td><td>OpenAI Chat Completions underlying protocol: request / response / streaming / tool call wire</td></tr><tr><td>[openai/OpenAI Protocol 02, Core Practices for AgentForge OpenAI Integration.md](/blog/01、model模型协议层/chatmodel/openai/OpenAI协议02、AgentForge 适配OpenAI接入核心实践)</td><td>AgentForge OpenAI Provider mapping, Builder, streaming aggregation, errors and evolution</td></tr><tr><td><a class="" href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/anthropic/Anthropic%E5%8D%8F%E8%AE%AE01%E3%80%81Anthropic%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3">anthropic/Anthropic Protocol 01, A Quick Understanding of the Anthropic Underlying Protocol.md</a></td><td>Anthropic Messages underlying protocol: top-level system field / content block / SSE / tool_use</td></tr><tr><td>[anthropic/Anthropic Protocol 02, Core Practices for AgentForge Anthropic Integration.md](/blog/01、model模型协议层/chatmodel/anthropic/Anthropic协议02、AgentForge 适配Anthropic接入核心实践)</td><td>AgentForge Anthropic Provider mapping, streaming aggregation, compatibility risks and evolution</td></tr></tbody></table>
<br>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="table-of-contents">Table of Contents<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#table-of-contents" class="hash-link" aria-label="Direct link to Table of Contents" title="Direct link to Table of Contents" translate="no">​</a></h2>
<ul>
<li class=""><a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#1-background-why-do-we-need-a-chatmodel-protocol-layer" class="">1. Background: Why Do We Need a ChatModel Protocol Layer?</a></li>
<li class=""><a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#2-core-concepts-what-does-a-standard-chatmodel-protocol-look-like" class="">2. Core Concepts: What Does a Standard ChatModel Protocol Look Like?</a></li>
<li class=""><a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#3-implementation-approach-how-agentforge-model-api-encapsulates-unified-inputoutput" class="">3. Implementation Approach: How agentforge-model-api Encapsulates Unified Input/Output</a></li>
<li class=""><a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#4-multi-protocol-extension-how-different-protocol-models-extend-based-on-the-standard-protocol" class="">4. Multi-Protocol Extension: How Different Protocol Models Extend Based on the Standard Protocol</a></li>
<li class=""><a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#5-hands-on-a-complete-multi-round-tool-call" class="">5. Hands-On: A Complete Multi-Round Tool Call</a></li>
<li class=""><a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#6-hands-on-use-cases-real-curl-and-return-results" class="">6. Hands-On Use Cases: Real curl and Return Results</a></li>
<li class=""><a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#7-verification-testing" class="">7. Verification Testing</a></li>
<li class=""><a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#8-boundaries-compatibility-and-evolution" class="">8. Boundaries, Compatibility, and Evolution</a></li>
<li class=""><a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#9-summary" class="">9. Summary</a></li>
<li class=""><a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#references" class="">References</a></li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-background-why-do-we-need-a-chatmodel-protocol-layer">1. Background: Why Do We Need a ChatModel Protocol Layer?<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#1-background-why-do-we-need-a-chatmodel-protocol-layer" class="hash-link" aria-label="Direct link to 1. Background: Why Do We Need a ChatModel Protocol Layer?" title="Direct link to 1. Background: Why Do We Need a ChatModel Protocol Layer?" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="11-starting-from-a-concrete-problem">1.1 Starting from a Concrete Problem<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#11-starting-from-a-concrete-problem" class="hash-link" aria-label="Direct link to 1.1 Starting from a Concrete Problem" title="Direct link to 1.1 Starting from a Concrete Problem" translate="no">​</a></h3>
<p>While developing AgentForge's Agent layer, we ran into this concrete problem:</p>
<blockquote>
<p>In the business code of the upper-layer ReAct Agent, today it is connected to OpenAI, tomorrow it may need to
connect to Anthropic, and the day after tomorrow it may need to connect to an enterprise internal gateway. How
can we ensure that when the model is swapped, not a single line of code in the Agent main loop needs to change?</p>
</blockquote>
<p>Without a unified abstraction, the most common way of writing this looks like:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// Not recommended: directly new-ing the vendor SDK inside the Agent</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">OpenAiClient</span><span class="token plain"> client </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">OpenAiClient</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">apiKey</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ChatCompletionResult</span><span class="token plain"> result </span><span class="token operator">=</span><span class="token plain"> client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">chatCompletion</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">messages</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"gpt-4o-mini"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">String</span><span class="token plain"> text </span><span class="token operator">=</span><span class="token plain"> result</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getChoices</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getContent</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>This piece of code binds three things tightly together: <strong>the vendor SDK type</strong>, <strong>the vendor request/response
structure</strong>, and <strong>the vendor field naming</strong>. Once you switch to Anthropic Messages (where system is a top-level
field, tools are called <code>tool_use</code>, and the return is an array of content blocks), the Agent layer has to be torn
down and rewritten.</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="12-what-happens-if-you-bind-directly-to-vendor-sdks">1.2 What Happens If You Bind Directly to Vendor SDKs?<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#12-what-happens-if-you-bind-directly-to-vendor-sdks" class="hash-link" aria-label="Direct link to 1.2 What Happens If You Bind Directly to Vendor SDKs?" title="Direct link to 1.2 What Happens If You Bind Directly to Vendor SDKs?" translate="no">​</a></h3>
<p>Let us list the cost of "not abstracting" one by one:</p>
<ul>
<li class=""><strong>Upper-layer pollution</strong>: Agent / Memory / Tool Calling are filled with branches like <code>if (provider == OPENAI)</code>;</li>
<li class=""><strong>Hard to test</strong>: every unit test needs a real API Key and a real network, so CI cannot run;</li>
<li class=""><strong>Expensive to extend</strong>: adding one more model vendor = changing one place in the upper-layer business code, and the regression scope is uncontrollable;</li>
<li class=""><strong>Protocol differences leak out</strong>: vendor-specific concepts such as <code>finish_reason</code>, <code>prompt_tokens</code>, <code>tool_calls</code> seep into the business code.</li>
</ul>
<blockquote>
<p><strong>Key point</strong>: The core value of the model protocol layer is not "supporting a few more models", but <strong>converging vendor differences within a replaceable boundary</strong>.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="13-the-four-goals-this-layer-must-achieve">1.3 The Four Goals This Layer Must Achieve<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#13-the-four-goals-this-layer-must-achieve" class="hash-link" aria-label="Direct link to 1.3 The Four Goals This Layer Must Achieve" title="Direct link to 1.3 The Four Goals This Layer Must Achieve" translate="no">​</a></h3>
<p>Therefore the positioning of <code>agentforge-model-api</code> is: <strong>the lowest-level, most stable model access abstraction
layer of AgentForge</strong>. It does not directly bind to OpenAI, Anthropic, or any third-party Java SDK; it only defines
unified request, response, message, streaming callback, HTTP transport, and exception boundaries.</p>
<p>Four design goals:</p>
<ol>
<li class=""><strong>Provider-neutral</strong>: upper-layer modules such as Agent, Memory, and Tool Calling depend only on the contract layer (<code>agentforge-model-api</code>) and core, not on any vendor SDK;</li>
<li class=""><strong>Protocol adaptation pushed down</strong>: differences such as OpenAI / Anthropic field names, Header, SSE, and finish reason are all absorbed by the Provider modules;</li>
<li class=""><strong>Java 8 API compatibility</strong>: the default development environment may use JDK 17, but the public LLM API avoids Java 9+ language features and collection factories;</li>
<li class=""><strong>Reserve extension points for subsequent Agent capabilities</strong>: message types, <code>customParameters</code>, <code>metadata</code>, and <code>HttpTransport</code> all retain room for extension.</li>
</ol>
<blockquote>
<p><strong>Note</strong>: Java 8 here means <strong>syntax and bytecode compatibility of the public API</strong>, and does not mean the project can only be built on JDK 8; the internal implementation is still recommended to run on JDK 17.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="14-from-request-to-response-the-protocol-layer-panorama">1.4 From Request to Response: The Protocol Layer Panorama<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#14-from-request-to-response-the-protocol-layer-panorama" class="hash-link" aria-label="Direct link to 1.4 From Request to Response: The Protocol Layer Panorama" title="Direct link to 1.4 From Request to Response: The Protocol Layer Panorama" translate="no">​</a></h3>
<p>The standard ChatModel protocol runs through the whole thing; the entire call chain is as follows:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Agent / Application</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">       │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">       ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">ChatModel / StreamingChatModel          &lt;- Unified entry: one set of interfaces</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">       │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">       ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">ChatRequest + ChatMessage + ChatRequestParameters   &lt;- Unified input</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">       │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">       ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Provider Adapter(OpenAI / Anthropic / ...)          &lt;- Only does protocol conversion</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">       │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">       ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">HttpTransport                           &lt;- Unified transport SPI</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">       │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">       ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Provider HTTP API  -&gt;  Provider JSON / SSE          &lt;- Vendor differences are isolated inside the Provider</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">       │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">       ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">ChatResponse + TokenUsage + FinishReason            &lt;- Unified output</span><br></div></code></pre></div></div>
<p>To summarize in one sentence: <strong>From the top you always see <code>ChatRequest</code> in and <code>ChatResponse</code> out; vendor protocols only exist inside the Provider modules.</strong></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-core-concepts-what-does-a-standard-chatmodel-protocol-look-like">2. Core Concepts: What Does a Standard ChatModel Protocol Look Like?<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#2-core-concepts-what-does-a-standard-chatmodel-protocol-look-like" class="hash-link" aria-label="Direct link to 2. Core Concepts: What Does a Standard ChatModel Protocol Look Like?" title="Direct link to 2. Core Concepts: What Does a Standard ChatModel Protocol Look Like?" translate="no">​</a></h2>
<p>Before writing code, first answer a question: <strong>What exactly does the so-called "standard ChatModel protocol" consist of?</strong></p>
<p>The answer can be broken down into four layers: module layering, package structure, core contracts, and the
behavioral constraints that Providers must follow.</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-module-layering-and-dependency-direction">2.1 Module Layering and Dependency Direction<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#21-module-layering-and-dependency-direction" class="hash-link" aria-label="Direct link to 2.1 Module Layering and Dependency Direction" title="Direct link to 2.1 Module Layering and Dependency Direction" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">agentforge-model</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── agentforge-model-api              # Contract layer: interfaces + req/vo, zero third-party dependencies</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── chat.ChatModel / StreamingChatModel</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── chat.message.*                # ChatMessage system + Content multimodal foundation</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── chat.request.*                # ChatRequest / ChatRequestParameters / ToolChoice</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── chat.response.*               # ChatResponse / StreamingChatResponseHandler / TokenUsage / FinishReason</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── tool / tool.spec / tool.execution / tool.error</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── http.*                        # HttpRequest / HttpResponse / HttpTransport / StreamingHttpResponseHandler</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   └── exception.ModelException</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── agentforge-model-core             # Vendor-independent default implementation and execution engine</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── chat.request.DefaultChatRequestParameters</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── http.JdkHttpTransport         # Based on HttpURLConnection</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── internal.json.Json            # Internal JSON capability (non-public API)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   └── tool.execution.ToolService / DefaultToolExecutor / ToolExecutionRequestUtil</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│       tool.spec.ToolSpecifications</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── agentforge-model-openai           # OpenAI Chat Completions protocol implementation</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── OpenAiChatModel / OpenAiStreamingChatModel</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   └── OpenAiMessages                # Package-internal shared wire helper (non-public API)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── agentforge-model-anthropic        # Anthropic Messages protocol implementation</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── AnthropicChatModel / AnthropicStreamingChatModel</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   └── AnthropicProtocol             # Blocking / Streaming shared wire mapping</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">└── agentforge-model-registry         # Out-of-the-box model factory</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    ├── LlmFactory / LlmEnum / LlmBasicConfig</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    └── models.IModel / BaseModel / OpenAiModel / AnthropicModel</span><br></div></code></pre></div></div>
<p>The dependency direction is strictly unidirectional and acyclic:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">            agentforge-model-api   （contract, no dependencies）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    ▲</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ┌───────────┼───────────────┐</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │           │               │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  model-core   model-openai   model-anthropic</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▲           ▲               ▲</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        └───────────┴───────────────┘</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                    │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            agentforge-model-registry</span><br></div></code></pre></div></div>
<table><thead><tr><th>Module</th><th>Responsibility</th><th>Contains vendor SDK</th></tr></thead><tbody><tr><td><code>agentforge-model-api</code></td><td>Stable contracts: interfaces, req/vo, messages, Tool contracts, HTTP SPI, exceptions</td><td>No</td></tr><tr><td><code>agentforge-model-core</code></td><td>Vendor-independent default implementation: parameter merging, JDK transport, JSON, Tool execution engine</td><td>No</td></tr><tr><td><code>agentforge-model-openai</code></td><td>OpenAI protocol mapping</td><td>No (hand-written wire, no SDK introduced)</td></tr><tr><td><code>agentforge-model-anthropic</code></td><td>Anthropic protocol mapping</td><td>No (hand-written wire, no SDK introduced)</td></tr><tr><td><code>agentforge-model-registry</code></td><td>Assembles Providers by configuration, provides a factory to the upper layer</td><td>No</td></tr></tbody></table>
<blockquote>
<p><strong>Key point</strong>: Core only defines a stable, Provider-neutral LLM boundary; Provider modules are only responsible
for protocol conversion, and absolutely must not leak vendor SDK types to the upper layer.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-contract-layer-package-structure">2.2 Contract Layer Package Structure<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#22-contract-layer-package-structure" class="hash-link" aria-label="Direct link to 2.2 Contract Layer Package Structure" title="Direct link to 2.2 Contract Layer Package Structure" translate="no">​</a></h3>
<p>The contract layer is the focus of this article, so let us expand it separately:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">cloud.changlu.agentforge.model</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── chat</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── ChatModel.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── StreamingChatModel.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── message</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   ├── ChatMessage.java / ChatMessageType.java / AbstractTextMessage.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   ├── SystemMessage.java / UserMessage.java / AiMessage.java / CustomMessage.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   ├── ToolExecutionRequest.java / ToolExecutionResultMessage.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   └── Content.java / ContentType.java / TextContent.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── request</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   ├── ChatRequest.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   ├── ChatRequestParameters.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   └── ToolChoice.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   └── response</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│       ├── ChatResponse.java / StreamingChatResponseHandler.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│       ├── TokenUsage.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│       └── FinishReason.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── tool</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── Tool.java / P.java / ReturnBehavior.java / ToolExecutor.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── spec</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   ├── ToolSpecification.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   └── ToolParameters.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── execution</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   ├── ToolExecution.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   └── ToolExecutionResult.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   └── error</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│       ├── ToolArgumentsException.java / ToolExecutionException.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│       ├── ToolArgumentsErrorHandler.java / ToolExecutionErrorHandler.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│       └── ToolErrorContext.java / ToolErrorHandlerResult.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── http</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── HttpRequest.java / HttpResponse.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── HttpTransport.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   └── StreamingHttpResponseHandler.java</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">└── exception</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    └── ModelException.java</span><br></div></code></pre></div></div>
<p><code>agentforge-model-core</code> holds the default implementations and execution engine (<strong>not in api</strong>):</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">cloud.changlu.agentforge.model</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── chat.request.DefaultChatRequestParameters   # Parameter default implementation + merge</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── http.JdkHttpTransport                       # HttpURLConnection transport implementation</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── internal.json.Json                          # Internal JSON (non-public API)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">└── tool</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    ├── spec.ToolSpecifications                 # @Tool method -&gt; ToolSpecification</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    └── execution.ToolService / DefaultToolExecutor / ToolExecutionRequestUtil</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Note</strong>: <code>api</code> is the "contract", <code>core</code> is the "default implementation". The upper layer depends on api to get
capabilities, and usually brings in core at runtime. Never treat implementation classes such as
<code>JdkHttpTransport</code>, <code>DefaultChatRequestParameters</code>, and <code>Json</code> as public APIs.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="23-what-core-contracts-make-up-the-standard-protocol">2.3 What Core Contracts Make Up the Standard Protocol?<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#23-what-core-contracts-make-up-the-standard-protocol" class="hash-link" aria-label="Direct link to 2.3 What Core Contracts Make Up the Standard Protocol?" title="Direct link to 2.3 What Core Contracts Make Up the Standard Protocol?" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Standard ChatModel protocol</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── Entry contract: ChatModel / StreamingChatModel</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── Input contract: ChatRequest + ChatRequestParameters (+ ToolChoice)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── Message contract: ChatMessage system (System / User / Ai / ToolResult / Custom + Content)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── Output contract: ChatResponse + TokenUsage + FinishReason</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── Streaming contract: StreamingChatResponseHandler (partial / thinking / complete / error)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── Transport contract: HttpTransport + StreamingHttpResponseHandler</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">└── Exception contract: ModelException</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-implementation-approach-how-agentforge-model-api-encapsulates-unified-inputoutput">3. Implementation Approach: How agentforge-model-api Encapsulates Unified Input/Output<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#3-implementation-approach-how-agentforge-model-api-encapsulates-unified-inputoutput" class="hash-link" aria-label="Direct link to 3. Implementation Approach: How agentforge-model-api Encapsulates Unified Input/Output" title="Direct link to 3. Implementation Approach: How agentforge-model-api Encapsulates Unified Input/Output" translate="no">​</a></h2>
<p>This chapter breaks down the interface encapsulation and implementation of the contract layer layer by layer.
The core idea is: <strong>input is uniformly converged into <code>ChatRequest</code>, output is uniformly converged into
<code>ChatResponse</code>, and synchronous and streaming share the same set of message and response models.</strong></p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-chatmodel-the-minimum-stable-boundary-of-a-synchronous-model">3.1 ChatModel: The Minimum Stable Boundary of a Synchronous Model<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#31-chatmodel-the-minimum-stable-boundary-of-a-synchronous-model" class="hash-link" aria-label="Direct link to 3.1 ChatModel: The Minimum Stable Boundary of a Synchronous Model" title="Direct link to 3.1 ChatModel: The Minimum Stable Boundary of a Synchronous Model" translate="no">​</a></h3>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@FunctionalInterface</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">interface</span><span class="token plain"> </span><span class="token class-name">ChatModel</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">ChatResponse</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">chat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ChatRequest</span><span class="token plain"> chatRequest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">chat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">String</span><span class="token plain"> userMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token class-name">ChatResponse</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">chat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ChatMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain"> messages</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token class-name">ChatResponse</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">chat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">List</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics operator">?</span><span class="token generics"> </span><span class="token generics keyword" style="color:rgb(189, 147, 249);font-style:italic">extends</span><span class="token generics"> </span><span class="token generics class-name">ChatMessage</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> messages</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Design points:</p>
<ul>
<li class="">A Provider <strong>only needs to implement one main entry</strong> <code>ChatResponse chat(ChatRequest)</code>;</li>
<li class="">The three convenience methods (single string, varargs messages, message list) are all <code>default</code>, and all eventually converge into <code>ChatRequest</code>, so the Provider does not need to implement multiple sets of entries repeatedly;</li>
<li class=""><code>@FunctionalInterface</code> lets simple model/test implementations use a Lambda directly.</li>
</ul>
<blockquote>
<p><strong>Key point</strong>: Converging convenience methods into <code>ChatRequest</code> is the key to "a Provider only implementing one
method". If every convenience method required the Provider to write its own, then once the number of Providers
grows, things would get out of control.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-streamingchatmodel-why-use-callbacks-for-streaming">3.2 StreamingChatModel: Why Use Callbacks for Streaming?<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#32-streamingchatmodel-why-use-callbacks-for-streaming" class="hash-link" aria-label="Direct link to 3.2 StreamingChatModel: Why Use Callbacks for Streaming?" title="Direct link to 3.2 StreamingChatModel: Why Use Callbacks for Streaming?" translate="no">​</a></h3>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">interface</span><span class="token plain"> </span><span class="token class-name">StreamingChatModel</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">chat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ChatRequest</span><span class="token plain"> chatRequest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">StreamingChatResponseHandler</span><span class="token plain"> handler</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">chat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">String</span><span class="token plain"> userMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">StreamingChatResponseHandler</span><span class="token plain"> handler</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">chat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">StreamingChatResponseHandler</span><span class="token plain"> handler</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">ChatMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain"> messages</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">chat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">List</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics operator">?</span><span class="token generics"> </span><span class="token generics keyword" style="color:rgb(189, 147, 249);font-style:italic">extends</span><span class="token generics"> </span><span class="token generics class-name">ChatMessage</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> messages</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">StreamingChatResponseHandler</span><span class="token plain"> handler</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p><strong>Why not use <code>Flow</code>, Reactive Streams, or Reactor?</strong> Let us compare several options:</p>
<table><thead><tr><th>Option</th><th>Advantages</th><th>Explanation of drawbacks</th></tr></thead><tbody><tr><td>Callback style (current choice)</td><td>Java 8 compatible, zero extra dependencies, intuitive implementation</td><td>Need to handle callback timing and exception convergence yourself</td></tr><tr><td><code>java.util.concurrent.Flow</code></td><td>JDK 9+ standard</td><td>Public API is forced to upgrade to JDK 9+, breaking Java 8 compatibility</td></tr><tr><td>Reactor / RxJava</td><td>Backpressure, powerful composition operators</td><td>The lowest-level protocol layer would strongly depend on a reactive framework, making dependencies heavy</td></tr><tr><td><code>CompletableFuture&lt;Stream&gt;</code></td><td>Simple</td><td>Streaming incremental semantics are expressed unnaturally, cancellation/error handling is cumbersome</td></tr></tbody></table>
<p>The final choice is the callback style, for the reason: <strong>the protocol layer must be light; the complexity of a reactive framework should not be pushed down to the lowest level.</strong></p>
<p>The streaming lifecycle is standardized as:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Start request</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   ├── onPartialThinking(partialThinking)  0..N times   (reasoning model thinking increment, optional)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   ├── onPartialResponse(partialText)      0..N times   (visible text increment)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   ├── onCompleteResponse(response)        exactly 1 time on success</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   └── onError(error)                      1 time on failure</span><br></div></code></pre></div></div>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">interface</span><span class="token plain"> </span><span class="token class-name">StreamingChatResponseHandler</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">onPartialResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">String</span><span class="token plain"> partialResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">onPartialThinking</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">String</span><span class="token plain"> partialThinking</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">onCompleteResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ChatResponse</span><span class="token plain"> completeResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">onError</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Throwable</span><span class="token plain"> error</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Note</strong>: <code>onPartialThinking</code> provides a default empty implementation, specifically to carry reasoning increments
such as DeepSeek-style <code>reasoning_content</code> and Anthropic <code>thinking</code> blocks. In this way, adding a new callback
also <strong>will not break existing implementations</strong> (they compile with zero changes).</p>
</blockquote>
<p><strong>Question prompt</strong>: Will the streaming and non-streaming result models split into two sets?</p>
<p>No. <code>onCompleteResponse</code> returns the <strong>already fully aggregated standard <code>ChatResponse</code></strong>, so streaming and
non-streaming ultimately reuse the unified response model; the Provider is responsible for aggregating tool call
increments, token usage, and finish reason into the <code>ChatResponse</code> before the stream ends.</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-chatrequest-immutable-unified-input">3.3 ChatRequest: Immutable Unified Input<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#33-chatrequest-immutable-unified-input" class="hash-link" aria-label="Direct link to 3.3 ChatRequest: Immutable Unified Input" title="Direct link to 3.3 ChatRequest: Immutable Unified Input" translate="no">​</a></h3>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">ChatRequest</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">List</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ChatMessage</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> messages</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">ChatRequestParameters</span><span class="token plain"> parameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Constraints:</p>
<ul>
<li class=""><code>messages</code> must contain at least one message, otherwise <code>IllegalArgumentException</code> is thrown at construction;</li>
<li class="">The Builder supports <code>message(single)</code> and <code>messages(collection)</code>;</li>
<li class="">After construction, the message list is wrapped immutably (<code>Collections.unmodifiableList</code>);</li>
<li class=""><code>parameters</code> may be empty, in which case all Provider Model Builder model-level default parameters are used.</li>
</ul>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">ChatRequest</span><span class="token plain"> request </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">ChatRequest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">SystemMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"You are a Java assistant."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">UserMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Explain CompletableFuture."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">parameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">DefaultChatRequestParameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">modelName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"provider-model"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">temperature</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">0.2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">maxTokens</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">1024</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="34-chatrequestparameters-parameter-contract-and-merge-rules">3.4 ChatRequestParameters: Parameter Contract and Merge Rules<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#34-chatrequestparameters-parameter-contract-and-merge-rules" class="hash-link" aria-label="Direct link to 3.4 ChatRequestParameters: Parameter Contract and Merge Rules" title="Direct link to 3.4 ChatRequestParameters: Parameter Contract and Merge Rules" translate="no">​</a></h3>
<table><thead><tr><th>Core field</th><th>Java type</th><th>Meaning</th></tr></thead><tbody><tr><td><code>modelName()</code></td><td><code>String</code></td><td>Provider model ID</td></tr><tr><td><code>temperature()</code></td><td><code>Double</code></td><td>Sampling randomness; whether it is supported is decided by the Provider</td></tr><tr><td><code>maxTokens()</code></td><td><code>Integer</code></td><td>Maximum number of output Tokens</td></tr><tr><td><code>topP()</code></td><td><code>Double</code></td><td>nucleus sampling parameter</td></tr><tr><td><code>stopSequences()</code></td><td><code>List&lt;String&gt;</code></td><td>Stop sequences</td></tr><tr><td><code>customParameters()</code></td><td><code>Map&lt;String,Object&gt;</code></td><td>Provider-specific extension fields</td></tr><tr><td><code>tools()</code></td><td><code>List&lt;ToolSpecification&gt;</code></td><td>Tool declarations the model is allowed to call for this request</td></tr><tr><td><code>toolChoice()</code></td><td><code>ToolChoice</code></td><td><code>AUTO</code> / <code>NONE</code> / <code>REQUIRED</code> / <code>SPECIFIC</code></td></tr><tr><td><code>toolChoiceName()</code></td><td><code>String</code></td><td>The tool name specified when <code>SPECIFIC</code></td></tr></tbody></table>
<p>The first six fields are abstract methods, and the last three are <strong>interface default methods</strong>:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">interface</span><span class="token plain"> </span><span class="token class-name">ChatRequestParameters</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">modelName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">Double</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">temperature</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">Integer</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">maxTokens</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">Double</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">topP</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">List</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">stopSequences</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">Object</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">customParameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token class-name">List</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ToolSpecification</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token class-name">ToolChoice</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">toolChoice</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">toolChoiceName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: Prefer to add new capabilities as <strong>interface default methods</strong>. In this way, even if a third
party has already implemented <code>ChatRequestParameters</code>, it can keep compiling with zero changes. This is the key
technique for a contract to keep evolving without breaking compatibility.</p>
</blockquote>
<p>Facing common parameters, a Provider has three legitimate ways to handle them:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Core common parameters</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   ├── Natively supported by Provider -&gt; map to vendor fields</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   ├── Not supported by Provider      -&gt; ignore or explicitly fail validation</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   └── Provider-specific capability   -&gt; extend via customParameters</span><br></div></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="341-parameter-merge-rules">3.4.1 Parameter Merge Rules<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#341-parameter-merge-rules" class="hash-link" aria-label="Direct link to 3.4.1 Parameter Merge Rules" title="Direct link to 3.4.1 Parameter Merge Rules" translate="no">​</a></h4>
<p><code>DefaultChatRequestParameters</code> (core module) is responsible for merging model-level defaults with request-level overrides:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Model Builder defaults</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">DefaultChatRequestParameters.merge(defaults, overrides)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▲</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Request overrides</span><br></div></code></pre></div></div>
<p>The rule is: <strong>a non-null request-level value overrides the model-level default value</strong>, implemented as "first apply
defaults, then apply overrides", where each field is written only when non-null; <code>customParameters</code> uses Map
merging, where a request-level key with the same name overrides the model-level key with the same name.</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token class-name">DefaultChatRequestParameters</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">merge</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">ChatRequestParameters</span><span class="token plain"> defaults</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">ChatRequestParameters</span><span class="token plain"> overrides</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">Builder</span><span class="token plain"> builder </span><span class="token operator">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token function" style="color:rgb(80, 250, 123)">apply</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> defaults</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token function" style="color:rgb(80, 250, 123)">apply</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> overrides</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Example:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// model-level</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">OpenAiChatModel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">modelName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"model-a"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">temperature</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">0.7</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">maxTokens</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">2048</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// request-level overrides only temperature</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">DefaultChatRequestParameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">temperature</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">0.1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// final: modelName=model-a, maxTokens=2048, temperature=0.1</span><br></div></code></pre></div></div>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="35-chatresponse--tokenusage--finishreason-unified-output">3.5 ChatResponse / TokenUsage / FinishReason: Unified Output<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#35-chatresponse--tokenusage--finishreason-unified-output" class="hash-link" aria-label="Direct link to 3.5 ChatResponse / TokenUsage / FinishReason: Unified Output" title="Direct link to 3.5 ChatResponse / TokenUsage / FinishReason: Unified Output" translate="no">​</a></h3>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">ChatResponse</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">AiMessage</span><span class="token plain"> aiMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">TokenUsage</span><span class="token plain"> tokenUsage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">FinishReason</span><span class="token plain"> finishReason</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">Object</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<table><thead><tr><th>Field</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td><code>aiMessage</code></td><td>Yes</td><td>The final model text / thinking / tool call message</td></tr><tr><td><code>tokenUsage</code></td><td>No</td><td>Standardized Token statistics</td></tr><tr><td><code>finishReason</code></td><td>No</td><td>Standardized finish reason</td></tr><tr><td><code>metadata</code></td><td>No</td><td>Retains Provider-original metadata that the common layer does not need to understand</td></tr></tbody></table>
<blockquote>
<p><strong>Key point</strong>: The Provider's raw JSON is not leaked upward directly, but key Provider information can be retained
in <code>metadata</code>. For example, OpenAI saves <code>id</code> / <code>model</code> / <code>created</code>, and Anthropic saves <code>id</code> / <code>model</code> / <code>type</code>.</p>
</blockquote>
<p>TokenUsage unifies three fields:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">TokenUsage</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">long</span><span class="token plain"> inputTokens</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">long</span><span class="token plain"> outputTokens</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">long</span><span class="token plain"> totalTokens</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">OpenAI:     prompt_tokens -&gt; inputTokens, completion_tokens -&gt; outputTokens, total_tokens -&gt; totalTokens</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Anthropic:  input_tokens  -&gt; inputTokens, output_tokens    -&gt; outputTokens, input + output -&gt; totalTokens</span><br></div></code></pre></div></div>
<p>FinishReason unified enum:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">enum</span><span class="token plain"> </span><span class="token class-name">FinishReason</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token constant" style="color:rgb(189, 147, 249)">STOP</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token constant" style="color:rgb(189, 147, 249)">LENGTH</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token constant" style="color:rgb(189, 147, 249)">TOOL_EXECUTION</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token constant" style="color:rgb(189, 147, 249)">CONTENT_FILTER</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token constant" style="color:rgb(189, 147, 249)">OTHER</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">OpenAI stop/length/tool_calls/content_filter -&gt; STOP/LENGTH/TOOL_EXECUTION/CONTENT_FILTER</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Anthropic end_turn|stop_sequence/max_tokens/tool_use -&gt; STOP/LENGTH/TOOL_EXECUTION</span><br></div></code></pre></div></div>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="36-chatmessage-the-message-system">3.6 ChatMessage: The Message System<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#36-chatmessage-the-message-system" class="hash-link" aria-label="Direct link to 3.6 ChatMessage: The Message System" title="Direct link to 3.6 ChatMessage: The Message System" translate="no">​</a></h3>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">interface</span><span class="token plain"> </span><span class="token class-name">ChatMessage</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">ChatMessageType</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">text</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">UnsupportedOperationException</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token string" style="color:rgb(255, 121, 198)">"Message type "</span><span class="token plain"> </span><span class="token operator">+</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">" does not expose a text payload"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Message types are uniformly registered by the <code>ChatMessageType</code> enum and bound to concrete implementation classes:</p>
<table><thead><tr><th>Type</th><th>Implementation class</th><th>Current semantics</th></tr></thead><tbody><tr><td><code>SYSTEM</code></td><td><code>SystemMessage</code></td><td>System instruction (inherits <code>AbstractTextMessage</code>)</td></tr><tr><td><code>USER</code></td><td><code>UserMessage</code></td><td>User input, can carry <code>name</code> and multimodal <code>List&lt;Content&gt;</code></td></tr><tr><td><code>AI</code></td><td><code>AiMessage</code></td><td>Model output / historical assistant message, can carry <code>thinking</code> and <code>toolExecutionRequests</code></td></tr><tr><td><code>TOOL_EXECUTION_RESULT</code></td><td><code>ToolExecutionResultMessage</code></td><td>Tool execution result</td></tr><tr><td><code>CUSTOM</code></td><td><code>CustomMessage</code></td><td>Provider-specific message</td></tr></tbody></table>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="361-aimessage">3.6.1 AiMessage<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#361-aimessage" class="hash-link" aria-label="Direct link to 3.6.1 AiMessage" title="Direct link to 3.6.1 AiMessage" translate="no">​</a></h4>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">AiMessage</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">implements</span><span class="token plain"> </span><span class="token class-name">ChatMessage</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> text</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> thinking</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">List</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">ToolExecutionRequest</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> toolExecutionRequests</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">Object</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> attributes</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<ul>
<li class=""><code>text()</code> may be <code>null</code> (during pure tool calling);</li>
<li class=""><code>thinking()</code> reserves a field position for reasoning content;</li>
<li class=""><code>hasToolExecutionRequests()</code> / <code>toolExecutionRequests()</code> expose tool calls;</li>
<li class=""><code>attributes()</code> provides a Provider extension position, defensively copied into a read-only Map.</li>
</ul>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="362-usermessage-and-the-content-multimodal-foundation">3.6.2 UserMessage and the Content Multimodal Foundation<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#362-usermessage-and-the-content-multimodal-foundation" class="hash-link" aria-label="Direct link to 3.6.2 UserMessage and the Content Multimodal Foundation" title="Direct link to 3.6.2 UserMessage and the Content Multimodal Foundation" translate="no">​</a></h4>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">UserMessage</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">implements</span><span class="token plain"> </span><span class="token class-name">ChatMessage</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">List</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">Content</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> contents</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<ul>
<li class=""><code>name</code> may be empty, and the Provider may ignore it;</li>
<li class=""><code>contents</code> must have at least one entry and is immutable after construction;</li>
<li class=""><code>Content</code> is the multimodal content base interface, and <code>type()</code> returns <code>ContentType</code>; currently only <code>TextContent</code> (<code>ContentType.TEXT</code>) is implemented, and <code>ImageContent</code> / <code>AudioContent</code> / <code>VideoContent</code> / <code>PdfFileContent</code> can be extended later <strong>without changing the message semantics</strong>.</li>
</ul>
<p>Retained historical compatibility accessors:</p>
<table><thead><tr><th>Method</th><th>Semantics</th></tr></thead><tbody><tr><td><code>text()</code></td><td>Returns the text when there is a single <code>TextContent</code>; otherwise throws <code>UnsupportedOperationException</code></td></tr><tr><td><code>hasSingleText()</code></td><td>Whether there is only one <code>TextContent</code></td></tr><tr><td><code>singleText()</code></td><td>Returns it when there is only one <code>TextContent</code>, otherwise throws an exception</td></tr></tbody></table>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">UserMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"hello"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">UserMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"alice"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"hello"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">UserMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">TextContent</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"describe this"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">TextContent</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"..."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">UserMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"alice"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">Arrays</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">Content</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token function" style="color:rgb(80, 250, 123)">asList</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">TextContent</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"hi"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="363-tool-messages-and-custom-messages">3.6.3 Tool Messages and Custom Messages<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#363-tool-messages-and-custom-messages" class="hash-link" aria-label="Direct link to 3.6.3 Tool Messages and Custom Messages" title="Direct link to 3.6.3 Tool Messages and Custom Messages" translate="no">​</a></h4>
<ul>
<li class=""><code>ToolExecutionRequest</code>: a single tool call initiated by the model, with fields <code>id</code> / <code>name</code> / <code>arguments</code> (raw JSON string);</li>
<li class=""><code>ToolExecutionResultMessage</code>: the tool execution result, with fields <code>id</code> / <code>toolName</code> / <code>text</code> / <code>isError</code> / <code>attributes</code>, where <code>id</code> must match the invoked <code>ToolExecutionRequest.id()</code>, used for correlation;</li>
<li class=""><code>CustomMessage</code>: carries messages that are Provider-specific and whose structure the framework cannot predefine; it only exposes an immutable <code>attributes()</code>. If a Provider does not support it, it should explicitly reject it rather than silently degrading it to an ordinary user message.</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="37-httptransport-transport-and-protocol-decoupling">3.7 HttpTransport: Transport and Protocol Decoupling<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#37-httptransport-transport-and-protocol-decoupling" class="hash-link" aria-label="Direct link to 3.7 HttpTransport: Transport and Protocol Decoupling" title="Direct link to 3.7 HttpTransport: Transport and Protocol Decoupling" translate="no">​</a></h3>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">interface</span><span class="token plain"> </span><span class="token class-name">HttpTransport</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">HttpResponse</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">execute</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">HttpRequest</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">throws</span><span class="token plain"> </span><span class="token class-name">IOException</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">executeStreaming</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">HttpRequest</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">StreamingHttpResponseHandler</span><span class="token plain"> handler</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        handler</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">onError</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">UnsupportedOperationException</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token string" style="color:rgb(255, 121, 198)">"Streaming HTTP is not supported by "</span><span class="token plain"> </span><span class="token operator">+</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">getClass</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>A Provider only depends on <code>HttpTransport</code>, so it can:</p>
<ul>
<li class="">Use <code>JdkHttpTransport</code> by default (based on <code>HttpURLConnection</code>);</li>
<li class="">Inject a Fake Transport during testing (this is the key to not depending on a real API Key);</li>
<li class="">Later adapt to OkHttp, Apache HttpClient, or an enterprise internal gateway;</li>
<li class="">Without modifying the <code>ChatModel</code> API at all.</li>
</ul>
<p>The underlying stream processing interface only knows HTTP lines and <strong>does not parse SSE</strong>:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">interface</span><span class="token plain"> </span><span class="token class-name">StreamingHttpResponseHandler</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">onOpen</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">int</span><span class="token plain"> statusCode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">List</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> headers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">onLine</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">String</span><span class="token plain"> line</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">onComplete</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">onError</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Throwable</span><span class="token plain"> error</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">HTTP line / SSE event</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Provider Stream Parser     &lt;- Vendor differences are absorbed here</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">partial text / thinking / ChatResponse</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: SSE is part of the vendor protocol and must be parsed by the OpenAI / Anthropic Provider itself.
Therefore, in the future, even if the Anthropic SSE event format is completely different from OpenAI's, there is
no need to modify Core.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="38-modelexception-and-internaljsonjson">3.8 ModelException and internal.json.Json<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#38-modelexception-and-internaljsonjson" class="hash-link" aria-label="Direct link to 3.8 ModelException and internal.json.Json" title="Direct link to 3.8 ModelException and internal.json.Json" translate="no">​</a></h3>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">ModelException</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">extends</span><span class="token plain"> </span><span class="token class-name">RuntimeException</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">Integer</span><span class="token plain"> statusCode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> responseBody</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>It can express both HTTP 4xx / 5xx and network IOException, JSON parsing failures, and Provider response
structures that do not match expectations. On HTTP non-2xx, it retains <code>message</code> / <code>statusCode</code> / <code>responseBody</code>,
making it convenient to later uniformly do retry, rate-limit, logging, and observability.</p>
<p>The JSON capability is placed in the <code>internal</code> package, which means it is <strong>not an AgentForge external public API</strong>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Java Map/List -&gt; JSON String</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">JSON String    -&gt; Map/List</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Note</strong>: In the future, even if the internal JSON implementation is replaced, it should not affect public
contracts such as <code>ChatModel</code>, <code>ChatRequest</code>, and <code>ChatResponse</code>.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-multi-protocol-extension-how-different-protocol-models-extend-based-on-the-standard-protocol">4. Multi-Protocol Extension: How Different Protocol Models Extend Based on the Standard Protocol<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#4-multi-protocol-extension-how-different-protocol-models-extend-based-on-the-standard-protocol" class="hash-link" aria-label="Direct link to 4. Multi-Protocol Extension: How Different Protocol Models Extend Based on the Standard Protocol" title="Direct link to 4. Multi-Protocol Extension: How Different Protocol Models Extend Based on the Standard Protocol" translate="no">​</a></h2>
<p>This is the core of this article. Once the standard protocol is defined, how do OpenAI, Anthropic, and any future
protocol extend based on it?</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="41-extension-iron-rules">4.1 Extension Iron Rules<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#41-extension-iron-rules" class="hash-link" aria-label="Direct link to 4.1 Extension Iron Rules" title="Direct link to 4.1 Extension Iron Rules" translate="no">​</a></h3>
<ol>
<li class=""><strong>Only add Provider modules</strong>, do not modify the existing contracts of <code>agentforge-model-api</code>;</li>
<li class=""><strong>Only implement <code>ChatModel</code> / <code>StreamingChatModel</code></strong>, do not redefine request/response types;</li>
<li class=""><strong>Vendor wire details must stay inside the Provider module</strong> (package-private helper);</li>
<li class="">When a new common capability is needed, prefer adding a <strong>default method</strong> to the interface;</li>
<li class="">Vendor-private fields go through <code>customParameters</code> / <code>metadata</code> / <code>attributes</code>, and do not pollute Core.</li>
</ol>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="42-provider-module-skeleton">4.2 Provider Module Skeleton<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#42-provider-module-skeleton" class="hash-link" aria-label="Direct link to 4.2 Provider Module Skeleton" title="Direct link to 4.2 Provider Module Skeleton" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">agentforge-model-xxx</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── pom.xml                       # Depends on agentforge-model-api (+ core)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">└── src/main/java/.../xxx</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    ├── XxxChatModel              implements ChatModel</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    ├── XxxStreamingChatModel     implements StreamingChatModel</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    └── XxxProtocol / XxxMessages # Package-private wire helper (shared by Blocking/Streaming)</span><br></div></code></pre></div></div>
<p>The pom dependencies of the existing modules:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">agentforge-model-openai     -&gt; agentforge-model-api + agentforge-model-core</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">agentforge-model-anthropic  -&gt; agentforge-model-api + agentforge-model-core</span><br></div></code></pre></div></div>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="43-hands-on-code-implementing-a-provider-in-five-steps">4.3 Hands-On Code: Implementing a Provider in Five Steps<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#43-hands-on-code-implementing-a-provider-in-five-steps" class="hash-link" aria-label="Direct link to 4.3 Hands-On Code: Implementing a Provider in Five Steps" title="Direct link to 4.3 Hands-On Code: Implementing a Provider in Five Steps" translate="no">​</a></h3>
<p>Regardless of the protocol, the implementation process is fixed at five steps:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">1. merge default parameters and request parameters</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">2. ChatRequest -&gt; Provider Request JSON</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">3. HttpTransport executes the request</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">4. Provider Response JSON -&gt; ChatResponse</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">5. Provider Error -&gt; ModelException</span><br></div></code></pre></div></div>
<p>Using <code>OpenAiChatModel</code> as the skeleton:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">OpenAiChatModel</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">implements</span><span class="token plain"> </span><span class="token class-name">ChatModel</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Override</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token class-name">ChatResponse</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">chat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ChatRequest</span><span class="token plain"> chatRequest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">// 1. merge</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">DefaultChatRequestParameters</span><span class="token plain"> parameters </span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token class-name">DefaultChatRequestParameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">merge</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">defaultParameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> chatRequest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">parameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token function" style="color:rgb(80, 250, 123)">requireModelName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">parameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">modelName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">// 2. ChatRequest -&gt; Provider Request JSON</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">Map</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">Object</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> payload </span><span class="token operator">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">buildPayload</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">chatRequest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> parameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">HttpRequest</span><span class="token plain"> request </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">HttpRequest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">baseUrl </span><span class="token operator">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/chat/completions"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">header</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Content-Type"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"application/json"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">body</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">stringify</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">payload</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">try</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token comment" style="color:rgb(98, 114, 164)">// 3. execute</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token class-name">HttpResponse</span><span class="token plain"> response </span><span class="token operator">=</span><span class="token plain"> httpTransport</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">execute</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token operator">!</span><span class="token plain">response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">isSuccessful</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">ModelException</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"OpenAI request failed with HTTP "</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token operator">+</span><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">statusCode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">statusCode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">body</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token comment" style="color:rgb(98, 114, 164)">// 4. Provider Response JSON -&gt; ChatResponse</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">parseResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">body</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">IOException</span><span class="token plain"> e</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token comment" style="color:rgb(98, 114, 164)">// 5. Provider Error -&gt; ModelException</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">ModelException</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"OpenAI request failed"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> e</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The Provider Builder is responsible for holding: <code>baseUrl</code> / <code>apiKey</code> / <code>anthropicVersion</code> / <code>modelName</code> /
sampling parameters / <code>customHeaders</code> / <code>httpTransport</code> / timeout, and at construction time collects these
parameters into the model-level <code>DefaultChatRequestParameters</code>.</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="44-request-side-mapping-comparison">4.4 Request-Side Mapping Comparison<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#44-request-side-mapping-comparison" class="hash-link" aria-label="Direct link to 4.4 Request-Side Mapping Comparison" title="Direct link to 4.4 Request-Side Mapping Comparison" translate="no">​</a></h3>
<p><strong>Question prompt</strong>: How big exactly is the difference between OpenAI and Anthropic? One look at this comparison table makes it clear.</p>
<table><thead><tr><th>Core</th><th>OpenAI (Chat Completions)</th><th>Anthropic (Messages)</th></tr></thead><tbody><tr><td><code>SystemMessage</code></td><td><code>messages[].role=system</code></td><td>Top-level <code>system</code> field (multiple system entries concatenated with blank lines)</td></tr><tr><td><code>UserMessage</code> (single text)</td><td><code>messages[].role=user, content=String</code></td><td><code>messages[].role=user, content=String</code></td></tr><tr><td><code>UserMessage</code> (multiple Content)</td><td><code>content=[{type:text,text}]</code></td><td><code>content=[{type:text,text}]</code></td></tr><tr><td><code>AiMessage</code> (with tool call)</td><td><code>role=assistant</code> + <code>tool_calls[]</code></td><td><code>role=assistant</code> + <code>content=[{type:tool_use,...}]</code></td></tr><tr><td><code>ToolExecutionResultMessage</code></td><td><code>role=tool</code> + <code>tool_call_id</code></td><td><code>role=user</code> + <code>content=[{type:tool_result,...}]</code></td></tr><tr><td><code>customParameters</code></td><td>Spread directly to the top level of the payload</td><td>Spread directly to the top level of the payload</td></tr><tr><td><code>tools</code></td><td><code>tools[].function.parameters</code></td><td><code>tools[].input_schema</code></td></tr><tr><td><code>toolChoice</code></td><td><code>"auto"/"none"/"required"/{type:function,...}</code></td><td><code>{type:auto/none/any/tool,...}</code></td></tr><tr><td><code>temperature/maxTokens/topP/stop</code></td><td><code>temperature/max_tokens/top_p/stop</code></td><td><code>temperature/max_tokens/top_p/stop_sequences</code></td></tr></tbody></table>
<p>The protocol difference points are exactly where the extension design must isolate:</p>
<ul>
<li class=""><strong>Different system position</strong>: OpenAI places it in the messages array, while Anthropic promotes it to a top-level field;</li>
<li class=""><strong>Different tool result role</strong>: OpenAI uses <code>role=tool</code>, while Anthropic uses a <code>tool_result</code> block under <code>role=user</code>;</li>
<li class=""><strong>Different tool_use input</strong>: OpenAI <code>arguments</code> is a JSON string, while Anthropic <code>input</code> is an object, requiring serialization alignment.</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="45-response-side-mapping-comparison">4.5 Response-Side Mapping Comparison<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#45-response-side-mapping-comparison" class="hash-link" aria-label="Direct link to 4.5 Response-Side Mapping Comparison" title="Direct link to 4.5 Response-Side Mapping Comparison" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">OpenAI assistant message                 Anthropic assistant message</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  tool_calls[] {id,type,                   content[] {type:"tool_use",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    function{name,arguments}}                id,name,input{...}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │                                        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼                                        ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   ToolExecutionRequest                     ToolExecutionRequest</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        └──────────────┬─────────────────────────┘</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                       ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            AiMessage.toolExecutionRequests()</span><br></div></code></pre></div></div>
<p>Key points:</p>
<ul>
<li class="">OpenAI <code>finish_reason=tool_calls</code> (older <code>function_call</code>) and Anthropic <code>stop_reason=tool_use</code> are uniformly mapped to <code>FinishReason.TOOL_EXECUTION</code>;</li>
<li class="">During pure tool calling, <code>content</code> may be <code>null</code>, in which case <code>AiMessage.text()</code> returns <code>null</code>;</li>
<li class="">Anthropic <code>tool_use.input</code> is an object, serialized into a JSON string and stored in <code>arguments</code>, aligned with OpenAI;</li>
<li class="">The original <code>id</code> / <code>model</code> / <code>created</code> / <code>type</code> go into <code>ChatResponse.metadata</code>.</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="46-streaming-aggregation">4.6 Streaming Aggregation<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#46-streaming-aggregation" class="hash-link" aria-label="Direct link to 4.6 Streaming Aggregation" title="Direct link to 4.6 Streaming Aggregation" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">HttpTransport.executeStreaming(request, state)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │  onOpen / onLine / onComplete / onError</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Provider Stream Parser (implements StreamingHttpResponseHandler)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ├── Text increment -&gt; handler.onPartialResponse(...)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ├── Thinking increment -&gt; handler.onPartialThinking(...)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        └── Tool increment -&gt; aggregated internally by index</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼ onCompleteResponse(aggregated ChatResponse)</span><br></div></code></pre></div></div>
<p><strong>Text and thinking are called back in real time, while tool calls are only exposed in the final response</strong>, consistent with LangChain4j.</p>
<p>OpenAI <code>delta.tool_calls[]</code> is bucketed by <code>index</code>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">chunk 1: {index:0, id:"call_1", function:{name:"getWeather", arguments:""}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">chunk 2: {index:0, function:{arguments:"{\"city\":"}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">chunk 3: {index:0, function:{arguments:"\"hangzhou\"}"}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼  merge by index</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">ToolExecutionRequest(id=call_1, name=getWeather, arguments={"city":"hangzhou"})</span><br></div></code></pre></div></div>
<ul>
<li class="">Most OpenAI-compatible endpoints carry <code>index</code>; when <code>index</code> is absent, "a new <code>id</code> appearing" is used as the start of a new call for fallback bucketing;</li>
<li class="">Multiple <code>arguments</code> fragments of the same <code>index</code> are concatenated in arrival order, and the raw JSON text is not reformatted.</li>
</ul>
<p>Anthropic opens a block with <code>content_block_start</code> (<code>tool_use</code>):</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">content_block_start (index=1, tool_use, id=toolu_1, name=get_weather)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">content_block_delta (index=1, input_json_delta: '{"city":')</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">content_block_delta (index=1, input_json_delta: '"hangzhou"}')</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼  accumulate by content block index</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">ToolExecutionRequest(id=toolu_1, name=get_weather, arguments={"city":"hangzhou"})</span><br></div></code></pre></div></div>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="47-shared-wire-helper">4.7 Shared Wire Helper<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#47-shared-wire-helper" class="hash-link" aria-label="Direct link to 4.7 Shared Wire Helper" title="Direct link to 4.7 Shared Wire Helper" translate="no">​</a></h3>
<p>It is recommended that each Provider extract a <strong>package-private</strong> wire helper for Blocking / Streaming reuse:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">OpenAiMessages（package-private, final）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── serialize(messages)          ChatMessage -&gt; OpenAI messages[]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── serializeTools(tools)        ToolSpecification -&gt; tools[]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── toolChoice(parameters)       ToolChoice -&gt; tool_choice</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── parseToolCalls(toolCalls)    tool_calls[] -&gt; ToolExecutionRequest</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">└── extractContent(content)      String | content[] -&gt; text</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">AnthropicProtocol（package-private, final）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── collectSystemMessages(messages)  top-level system field</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── serializeMessages(messages)      messages[]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── serializeTools(tools)            input_schema</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── extractToolUses(contentBlocks)   tool_use -&gt; ToolExecutionRequest</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">└── extractText(contentBlocks)       text block -&gt; text</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: It is an implementation detail of the Provider, <strong>not a public API</strong>, so it will not pollute <code>agentforge-model-api</code>.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="48-model-registration-and-factory">4.8 Model Registration and Factory<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#48-model-registration-and-factory" class="hash-link" aria-label="Direct link to 4.8 Model Registration and Factory" title="Direct link to 4.8 Model Registration and Factory" translate="no">​</a></h3>
<p><strong>Question prompt</strong>: When the upper layer wants to connect a model, does it still have to <code>new</code> a concrete <code>OpenAiChatModel</code>?</p>
<p>No. <code>agentforge-model-registry</code> provides out-of-the-box assembly capabilities on top of the Providers:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">LlmBasicConfig（provider / url / modelName / apiKey / props）</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">LlmFactory.buildChatModel(config) / buildStreamChatModel(config)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │  LlmEnum.of(providerCode) -&gt; IModel</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">OpenAiModel / AnthropicModel  implement IModel</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">OpenAiChatModel / AnthropicChatModel  (underlying still the standard ChatModel)</span><br></div></code></pre></div></div>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">interface</span><span class="token plain"> </span><span class="token class-name">IModel</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">ChatModel</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">buildChatModel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">LlmBasicConfig</span><span class="token plain"> llmBasicConfig</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">StreamingChatModel</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">buildStreamChatModel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">LlmBasicConfig</span><span class="token plain"> llmBasicConfig</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p><code>LlmEnum</code> registers "providerCode -&gt; IModel implementation class", and <code>LlmFactory</code> reflectively instantiates and
delegates construction, for example <code>OPENAI(1, "OpenAI", OpenAiModel.class)</code>, <code>ANTHROPIC(2, "Anthropic", AnthropicModel.class)</code>.</p>
<blockquote>
<p><strong>Note</strong>: The <code>IModel</code> comment has already reserved room: subsequent <strong>other model categories</strong> such as
<code>EmbeddingModel</code> will also continue to be extended here. This means that the pattern of "standard protocol +
Provider extension" will continue to be replicated along model categories.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="49-new-provider-checklist">4.9 New Provider Checklist<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#49-new-provider-checklist" class="hash-link" aria-label="Direct link to 4.9 New Provider Checklist" title="Direct link to 4.9 New Provider Checklist" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">[ ] Create a new agentforge-model-xxx module, depending on api (+ core)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[ ] XxxChatModel implements ChatModel</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[ ] XxxStreamingChatModel implements StreamingChatModel</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[ ] XxxProtocol / XxxMessages package-private wire helper (shared by Blocking/Streaming)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[ ] Implement in five steps: merge -&gt; request -&gt; transport -&gt; response -&gt; exception</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[ ] Map FinishReason / TokenUsage / metadata / tool calls</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[ ] Collect model parameters into DefaultChatRequestParameters defaults</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[ ] Add unit tests: plain text / tool call / streaming aggregation / errors / parameter merge</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[ ] Add an IModel implementation in agentforge-model-registry and register it in LlmEnum</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-hands-on-a-complete-multi-round-tool-call">5. Hands-On: A Complete Multi-Round Tool Call<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#5-hands-on-a-complete-multi-round-tool-call" class="hash-link" aria-label="Direct link to 5. Hands-On: A Complete Multi-Round Tool Call" title="Direct link to 5. Hands-On: A Complete Multi-Round Tool Call" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="51-why-is-a-tool-layer-still-needed">5.1 Why Is a Tool Layer Still Needed?<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#51-why-is-a-tool-layer-still-needed" class="hash-link" aria-label="Direct link to 5.1 Why Is a Tool Layer Still Needed?" title="Direct link to 5.1 Why Is a Tool Layer Still Needed?" translate="no">​</a></h3>
<p><strong>Question prompt</strong>: The Provider can already parse <code>tool_calls</code>, so why is there still a set of <code>ToolService</code> in the Model API?</p>
<p>Because the two solve different problems:</p>
<ul>
<li class="">The Provider layer solves "<strong>how tool calls are expressed in the model protocol</strong>" (wire mapping);</li>
<li class="">The Tool layer solves "<strong>how the upper-layer Agent turns an ordinary Java method into a tool callable by the LLM, and automatically drives multi-round execution</strong>".</li>
</ul>
<p>It corresponds to the package <code>cloud.changlu.agentforge.model.tool</code>, split into the top level + three subpackages:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">cloud.changlu.agentforge.model.tool</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── Tool / P / ReturnBehavior / ToolExecutor          # Core contracts</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── spec</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── ToolSpecification                             # name/description/parameters/strict</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── ToolParameters                               # JSON-Schema style (pure Map, no third-party dependencies)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   └── ToolSpecifications（core）                    # @Tool method -&gt; ToolSpecification</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── execution</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── ToolExecution / ToolExecutionResult（api）    # A single execution (request+result+elapsed) / value object</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── ToolService / DefaultToolExecutor（core）     # Registration + reasoning/execution loop / reflective executor</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   └── ToolExecutionRequestUtil（core）              # arguments JSON -&gt; Map</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">└── error</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    ├── ToolArgumentsException / ToolExecutionException</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    └── ToolArgumentsErrorHandler / ToolExecutionErrorHandler</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ToolErrorContext / ToolErrorHandlerResult</span><br></div></code></pre></div></div>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="52-quickly-building-a-tool">5.2 Quickly Building a Tool<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#52-quickly-building-a-tool" class="hash-link" aria-label="Direct link to 5.2 Quickly Building a Tool" title="Direct link to 5.2 Quickly Building a Tool" translate="no">​</a></h3>
<p>Annotate a method with <code>@Tool</code> and hand it to <code>ToolService</code> for registration:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">WeatherTools</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">value </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Returns the weather for the given city"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">getWeather</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@P</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"city name"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> city</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Weather in "</span><span class="token plain"> </span><span class="token operator">+</span><span class="token plain"> city </span><span class="token operator">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">": 22C sunny"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">ToolService</span><span class="token plain"> toolService </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">ToolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Arrays</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">asList</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">WeatherTools</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(98, 114, 164)">// Scan all @Tool methods</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ToolChatResult</span><span class="token plain"> result </span><span class="token operator">=</span><span class="token plain"> toolService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">chat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">model</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> parameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> messages</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">result</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">finalResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">aiMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">text</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p><code>ToolSpecifications</code> automatically converts the method name / <code>@P</code> parameter names / types into a
<code>ToolSpecification</code> + <code>ToolParameters</code>, naturally docking with the Provider-side <code>tools()</code> / <code>toolChoice()</code>.</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="53-toolexecutor-and-the-execution-loop">5.3 ToolExecutor and the Execution Loop<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#53-toolexecutor-and-the-execution-loop" class="hash-link" aria-label="Direct link to 5.3 ToolExecutor and the Execution Loop" title="Direct link to 5.3 ToolExecutor and the Execution Loop" translate="no">​</a></h3>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@FunctionalInterface</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">interface</span><span class="token plain"> </span><span class="token class-name">ToolExecutor</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">execute</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ToolExecutionRequest</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">Object</span><span class="token plain"> memoryId</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token class-name">ToolExecutionResult</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">executeWithResult</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ToolExecutionRequest</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">Object</span><span class="token plain"> memoryId</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<ul>
<li class=""><code>DefaultToolExecutor</code> (core) is the default reflective implementation: it binds the <code>arguments()</code> JSON to the <code>@Tool</code> method parameters and invokes it;<!-- -->
<ul>
<li class=""><code>String</code> -&gt; returned as-is;</li>
<li class=""><code>void</code> -&gt; the literal <code>"Success"</code>;</li>
<li class="">others -&gt; <code>Json.stringify(...)</code>.</li>
</ul>
</li>
<li class="">You can also use a Lambda to provide a custom <code>ToolExecutor</code>.</li>
</ul>
<p>The loop of <code>ToolService.chat(...)</code>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">1. Call ChatModel with the registered tools</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">2. If aiMessage.hasToolExecutionRequests():</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     a. Execute tools one by one (if no executor found -&gt; throw an exception by default)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     b. Convert each ToolExecutionResult into a ToolExecutionResultMessage (correlated by id)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     c. Append to the message list</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     d. If ReturnBehavior requires an immediate return -&gt; stop</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     e. Otherwise continue to the next round with the new messages</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">3. If there is no tool call -&gt; return the final ChatResponse + all ToolExecution</span><br></div></code></pre></div></div>
<ul>
<li class="">Default <code>maxToolCallingRoundTrips = 100</code>, preventing infinite loops;</li>
<li class=""><code>ReturnBehavior</code>: <code>TO_LLM</code> (default), <code>IMMEDIATE</code>, <code>IMMEDIATE_IF_LAST</code>;</li>
<li class="">Any tool error forces another round to be run, so that the LLM can see the error and correct and retry.</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="54-error-handling">5.4 Error Handling<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#54-error-handling" class="hash-link" aria-label="Direct link to 5.4 Error Handling" title="Direct link to 5.4 Error Handling" translate="no">​</a></h3>
<table><thead><tr><th>Exception</th><th>Trigger point</th><th>Default handling</th></tr></thead><tbody><tr><td><code>ToolArgumentsException</code></td><td>Arguments JSON cannot be parsed / type mismatch / missing required parameter</td><td>Throw an exception (<code>RETHROW</code>)</td></tr><tr><td><code>ToolExecutionException</code></td><td>Tool method execution fails</td><td>Return <code>ToolErrorHandlerResult.text(...)</code> to the LLM</td></tr></tbody></table>
<p>You can customize via <code>argumentsErrorHandler(...)</code> / <code>executionErrorHandler(...)</code>: either return
<code>ToolErrorHandlerResult.text(msg)</code> (returned to the LLM), or directly throw an exception (terminating the call).</p>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="55-relationship-with-the-function-calling-protocol-layer">5.5 Relationship with the Function Calling Protocol Layer<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#55-relationship-with-the-function-calling-protocol-layer" class="hash-link" aria-label="Direct link to 5.5 Relationship with the Function Calling Protocol Layer" title="Direct link to 5.5 Relationship with the Function Calling Protocol Layer" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">ToolService.chat()                     &lt;- Upper-layer Agent entry (reuses this chapter's Tool layer)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">ChatModel / ChatRequest / ChatResponse &lt;- Chapter 3/4 standard protocol</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">OpenAI / Anthropic Adapter             &lt;- Chapter 4 multi-protocol extension</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">AiMessage.toolExecutionRequests()      &lt;- The tool calls given by the model</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">ToolService executes -&gt; ToolExecutionResultMessage filled back</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-hands-on-use-cases-real-curl-and-return-results">6. Hands-On Use Cases: Real curl and Return Results<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#6-hands-on-use-cases-real-curl-and-return-results" class="hash-link" aria-label="Direct link to 6. Hands-On Use Cases: Real curl and Return Results" title="Direct link to 6. Hands-On Use Cases: Real curl and Return Results" translate="no">​</a></h2>
<p>This chapter walks completely through the most common call forms — <strong>non-streaming / streaming</strong> and <strong>plain
text / Function Calling / thinking process</strong> — with real curl and return results, for easy cross-reference with
the <code>ChatRequest</code> / <code>ChatResponse</code> mapping in Chapter 3 and the protocol conversion in Chapter 4. On the OpenAI
side, it additionally covers two types of returns: "tool calling" and "reasoning model thinking process".</p>
<blockquote>
<p>Note: The return values below are <strong>real protocol structures</strong> (<code>id</code>, timestamps, and token counts will change
with the actual call); the repository provides runnable <code>openai/verify-openai-chat.sh</code> and
<code>anthropic/verify-anthropic-chat.sh</code> for real-environment verification.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="61-openai">6.1 OpenAI<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#61-openai" class="hash-link" aria-label="Direct link to 6.1 OpenAI" title="Direct link to 6.1 OpenAI" translate="no">​</a></h3>
<p>This section covers 4 types of OpenAI returns: non-streaming plain text, non-streaming Function Calling,
streaming thinking process, and streaming Function Calling.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="611-non-streaming-plain-text">6.1.1 Non-Streaming (Plain Text)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#611-non-streaming-plain-text" class="hash-link" aria-label="Direct link to 6.1.1 Non-Streaming (Plain Text)" title="Direct link to 6.1.1 Non-Streaming (Plain Text)" translate="no">​</a></h4>
<p>curl:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> https://api.openai.com/v1/chat/completions </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Authorization: Bearer </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$OPENAI_API_KEY</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Content-Type: application/json"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-d</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'{</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "model": "gpt-4o-mini",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "messages": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      {"role": "system", "content": "You are a helpful assistant."},</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      {"role": "user", "content": "用一句话解释什么是 CompletableFuture。"}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "temperature": 0.7,</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "max_tokens": 256</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">  }'</span><br></div></code></pre></div></div>
<p>Return (excerpt):</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"chatcmpl-9xYzAbC123"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"object"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"chat.completion"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"created"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">1726200000</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"model"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"gpt-4o-mini"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"choices"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"index"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"CompletableFuture 是 Java 8 引入的异步编程工具，用来表示一个尚未完成、但未来会给出结果的计算。"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"finish_reason"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"stop"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"usage"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"prompt_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">28</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"completion_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">38</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"total_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">66</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>AgentForge-side mapping:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">choices[0].message.content -&gt; AiMessage.text()</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">choices[0].finish_reason   -&gt; FinishReason.STOP</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">usage.prompt_tokens        -&gt; TokenUsage.inputTokens  = 28</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">usage.completion_tokens    -&gt; TokenUsage.outputTokens = 38</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">usage.total_tokens         -&gt; TokenUsage.totalTokens  = 66</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">id / model / created       -&gt; ChatResponse.metadata</span><br></div></code></pre></div></div>
<p>Corresponding call:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">ChatModel</span><span class="token plain"> model </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">OpenAiChatModel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">apiKey</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getenv</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"OPENAI_API_KEY"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">modelName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"gpt-4o-mini"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">temperature</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">0.7</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">maxTokens</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">256</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ChatResponse</span><span class="token plain"> response </span><span class="token operator">=</span><span class="token plain"> model</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">chat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ChatRequest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">SystemMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"You are a helpful assistant."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">UserMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"用一句话解释什么是 CompletableFuture。"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">aiMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">text</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tokenUsage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">totalTokens</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(98, 114, 164)">// 66</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">finishReason</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">                </span><span class="token comment" style="color:rgb(98, 114, 164)">// STOP</span><br></div></code></pre></div></div>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="612-non-streaming-function-calling">6.1.2 Non-Streaming (Function Calling)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#612-non-streaming-function-calling" class="hash-link" aria-label="Direct link to 6.1.2 Non-Streaming (Function Calling)" title="Direct link to 6.1.2 Non-Streaming (Function Calling)" translate="no">​</a></h4>
<p>curl (carrying <code>tools</code> and <code>tool_choice</code>):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> https://api.openai.com/v1/chat/completions </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Authorization: Bearer </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$OPENAI_API_KEY</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Content-Type: application/json"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-d</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'{</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "model": "gpt-4o-mini",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "messages": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      {"role": "user", "content": "杭州今天天气怎么样？"}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "tools": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        "type": "function",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        "function": {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "name": "get_weather",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "description": "查询指定城市的当前天气",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "parameters": {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "type": "object",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "properties": { "city": { "type": "string", "description": "城市名" } },</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "required": ["city"]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "tool_choice": "auto"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">  }'</span><br></div></code></pre></div></div>
<p>Return (the model decides to call the tool, <code>content</code> is <code>null</code>):</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"chatcmpl-9xYzAbC456"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"object"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"chat.completion"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"created"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">1726200100</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"model"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"gpt-4o-mini"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"choices"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"index"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"message"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token null keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"tool_calls"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"call_abc123"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"function"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token property">"function"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">              </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">              </span><span class="token property">"arguments"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"{\"city\":\"杭州\"}"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"finish_reason"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tool_calls"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"usage"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"prompt_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">62</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"completion_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">18</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"total_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">80</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>AgentForge-side mapping:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">message.tool_calls[].id                 -&gt; ToolExecutionRequest.id        = "call_abc123"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">message.tool_calls[].function.name      -&gt; ToolExecutionRequest.name      = "get_weather"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">message.tool_calls[].function.arguments -&gt; ToolExecutionRequest.arguments = {"city":"杭州"}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">message.content = null                  -&gt; AiMessage.text()               = null</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">finish_reason=tool_calls                -&gt; FinishReason.TOOL_EXECUTION</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">usage.*                                 -&gt; TokenUsage</span><br></div></code></pre></div></div>
<p>Corresponding call and "tool result fill-back":</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">ToolSpecification</span><span class="token plain"> weather </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">ToolSpecification</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"get_weather"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"查询指定城市的当前天气"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">parameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ToolParameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">addProperty</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"city"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"string"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"城市名"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ChatResponse</span><span class="token plain"> first </span><span class="token operator">=</span><span class="token plain"> model</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">chat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ChatRequest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">UserMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"杭州今天天气怎么样？"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">parameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">DefaultChatRequestParameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tool</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">weather</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">toolChoice</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ToolChoice</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">AUTO</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// The model returns a tool call: AiMessage.text() == null, finishReason = TOOL_EXECUTION</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ToolExecutionRequest</span><span class="token plain"> call </span><span class="token operator">=</span><span class="token plain"> first</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">aiMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">toolExecutionRequests</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">call</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">// get_weather</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">call</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">arguments</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(98, 114, 164)">// {"city":"杭州"}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// After executing the tool, fill the result back as a ToolExecutionResultMessage (correlated by id), then start the second round</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ChatResponse</span><span class="token plain"> second </span><span class="token operator">=</span><span class="token plain"> model</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">chat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ChatRequest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">UserMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"杭州今天天气怎么样？"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">first</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">aiMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ToolExecutionResultMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">call</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> call</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"杭州今天 22℃，天气晴。"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">second</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">aiMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">text</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(98, 114, 164)">// "杭州今天 22℃，天气晴。"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">second</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">finishReason</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">       </span><span class="token comment" style="color:rgb(98, 114, 164)">// STOP</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: The tool call request and the tool execution result are correlated via <code>id</code>; <code>AiMessage</code> holds both
text and <code>toolExecutionRequests()</code>, so a mixed output of "text + tool call" can also be expressed.</p>
</blockquote>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="613-streaming-plain-text--thinking-process">6.1.3 Streaming (Plain Text + Thinking Process)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#613-streaming-plain-text--thinking-process" class="hash-link" aria-label="Direct link to 6.1.3 Streaming (Plain Text + Thinking Process)" title="Direct link to 6.1.3 Streaming (Plain Text + Thinking Process)" translate="no">​</a></h4>
<p>Taking an OpenAI-compatible reasoning model as an example (such as the DeepSeek series, where the thinking increment is placed in <code>reasoning_content</code>):</p>
<p>curl (<code>-N</code> disables buffering, continuously outputting SSE):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-N</span><span class="token plain"> https://api.openai.com/v1/chat/completions </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Authorization: Bearer </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$OPENAI_API_KEY</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Content-Type: application/json"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-d</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'{</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "model": "deepseek-reasoner",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "stream": true,</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "stream_options": { "include_usage": true },</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "messages": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      {"role": "user", "content": "用一句话解释什么是 CompletableFuture。"}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">  }'</span><br></div></code></pre></div></div>
<p>Return (<code>data:</code> frames, <code>reasoning_content</code> first and then <code>content</code>):</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"id":"chatcmpl-9xYzAbC789","object":"chat.completion.chunk","created":1726200200,"model":"deepseek-reasoner","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"用户想要一句话解释","content":null},"finish_reason":null}]}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"id":"chatcmpl-9xYzAbC789","object":"chat.completion.chunk","created":1726200200,"model":"deepseek-reasoner","choices":[{"index":0,"delta":{"reasoning_content":"CompletableFuture，我需要给出准确又简洁的定义。","content":null},"finish_reason":null}]}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"id":"chatcmpl-9xYzAbC789","object":"chat.completion.chunk","created":1726200200,"model":"deepseek-reasoner","choices":[{"index":0,"delta":{"content":"CompletableFuture"},"finish_reason":null}]}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"id":"chatcmpl-9xYzAbC789","object":"chat.completion.chunk","created":1726200200,"model":"deepseek-reasoner","choices":[{"index":0,"delta":{"content":" 是 Java 8 引入的异步编程工具。"},"finish_reason":null}]}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"id":"chatcmpl-9xYzAbC789","object":"chat.completion.chunk","created":1726200200,"model":"deepseek-reasoner","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"id":"chatcmpl-9xYzAbC789","object":"chat.completion.chunk","created":1726200200,"model":"deepseek-reasoner","choices":[],"usage":{"prompt_tokens":14,"completion_tokens":52,"total_tokens":66}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: [DONE]</span><br></div></code></pre></div></div>
<p>AgentForge-side callback sequence:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">onPartialThinking("用户想要一句话解释")</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">onPartialThinking("CompletableFuture，我需要给出准确又简洁的定义。")</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">onPartialResponse("CompletableFuture")</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">onPartialResponse(" 是 Java 8 引入的异步编程工具。")</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">onCompleteResponse(ChatResponse{ text="CompletableFuture 是 Java 8 引入的异步编程工具。",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                thinking="用户想要一句话解释CompletableFuture，我需要给出准确又简洁的定义。",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                finishReason=STOP, tokenUsage=(14,52,66) })</span><br></div></code></pre></div></div>
<p>Corresponding call:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">StreamingChatModel</span><span class="token plain"> model </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">OpenAiStreamingChatModel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">apiKey</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getenv</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"OPENAI_API_KEY"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">modelName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"deepseek-reasoner"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">model</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">chat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"用一句话解释什么是 CompletableFuture。"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">StreamingChatResponseHandler</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Override</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">onPartialThinking</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">String</span><span class="token plain"> partialThinking</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">print</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"[思考] "</span><span class="token plain"> </span><span class="token operator">+</span><span class="token plain"> partialThinking</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(98, 114, 164)">// Thinking increment, optionally displayed</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Override</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">onPartialResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">String</span><span class="token plain"> partialResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">print</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">partialResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">               </span><span class="token comment" style="color:rgb(98, 114, 164)">// Formal answer, output character by character</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Override</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">onCompleteResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ChatResponse</span><span class="token plain"> completeResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"thinking="</span><span class="token plain"> </span><span class="token operator">+</span><span class="token plain"> completeResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">aiMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">thinking</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"done="</span><span class="token plain"> </span><span class="token operator">+</span><span class="token plain"> completeResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">finishReason</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Override</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">onError</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Throwable</span><span class="token plain"> error</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        error</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">printStackTrace</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<blockquote>
<p>Note: The <code>[DONE]</code> frame only indicates the end of SSE; the real aggregated result is given by
<code>onCompleteResponse</code> after the last chunk that carries <code>usage</code>. <code>stream_options.include_usage=true</code> is the
prerequisite for getting <code>usage</code>.</p>
</blockquote>
<blockquote>
<p><strong>Key point</strong>: <code>reasoning_content</code> (some implementations use <code>thinking</code>) is recognized as a thinking increment
and goes through <code>onPartialThinking</code> in real time, and is finally aggregated into <code>AiMessage.thinking()</code>; the
visible answer goes through <code>onPartialResponse</code>, and the two channels do not interfere with each other.</p>
</blockquote>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="614-streaming-function-calling-increment-aggregation">6.1.4 Streaming (Function Calling Increment Aggregation)<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#614-streaming-function-calling-increment-aggregation" class="hash-link" aria-label="Direct link to 6.1.4 Streaming (Function Calling Increment Aggregation)" title="Direct link to 6.1.4 Streaming (Function Calling Increment Aggregation)" translate="no">​</a></h4>
<p>curl (<code>stream: true</code> + <code>tools</code>):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-N</span><span class="token plain"> https://api.openai.com/v1/chat/completions </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Authorization: Bearer </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$OPENAI_API_KEY</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Content-Type: application/json"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-d</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'{</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "model": "gpt-4o-mini",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "stream": true,</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "stream_options": { "include_usage": true },</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "messages": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      {"role": "user", "content": "杭州今天天气怎么样？"}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "tools": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        "type": "function",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        "function": {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "name": "get_weather",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "description": "查询指定城市的当前天气",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          "parameters": {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "type": "object",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "properties": { "city": { "type": "string", "description": "城市名" } },</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">            "required": ["city"]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">          }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">        }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      }</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "tool_choice": "auto"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">  }'</span><br></div></code></pre></div></div>
<p>Return (<code>delta.tool_calls[]</code> arrives in fragments by <code>index</code>):</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"id":"chatcmpl-9xYzAbC101","object":"chat.completion.chunk","created":1726200300,"model":"gpt-4o-mini","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_abc123","type":"function","function":{"name":"get_weather","arguments":""}}]},"finish_reason":null}]}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"id":"chatcmpl-9xYzAbC101","object":"chat.completion.chunk","created":1726200300,"model":"gpt-4o-mini","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"city\":"}}]},"finish_reason":null}]}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"id":"chatcmpl-9xYzAbC101","object":"chat.completion.chunk","created":1726200300,"model":"gpt-4o-mini","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"杭州\"}"}}]},"finish_reason":null}]}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"id":"chatcmpl-9xYzAbC101","object":"chat.completion.chunk","created":1726200300,"model":"gpt-4o-mini","choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}]}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"id":"chatcmpl-9xYzAbC101","object":"chat.completion.chunk","created":1726200300,"model":"gpt-4o-mini","choices":[],"usage":{"prompt_tokens":62,"completion_tokens":18,"total_tokens":80}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: [DONE]</span><br></div></code></pre></div></div>
<p>AgentForge-side aggregation result:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Concatenate arguments fragment by fragment by index=0:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  id        = "call_abc123"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  name      = "get_weather"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  arguments = "{\"city\":" + "\"杭州\"}" = {"city":"杭州"}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼ onCompleteResponse</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">ChatResponse{ text=null,</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">              toolExecutionRequests=[ToolExecutionRequest(id=call_abc123, name=get_weather, arguments={"city":"杭州"})],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">              finishReason=TOOL_EXECUTION }</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Note</strong>: In streaming, tool calls are not exposed via <code>onPartialResponse</code>; they are given all at once on
<code>AiMessage.toolExecutionRequests()</code> of <code>onCompleteResponse</code>. Most OpenAI-compatible endpoints carry <code>index</code>;
when absent, a newly appearing <code>id</code> is used as the start of a new call for fallback bucketing.</p>
</blockquote>
<p><strong>Supplement: thinking + tool call in the same stream</strong>. When an OpenAI-compatible reasoning model carries <code>tools</code>,
it may first stream <code>reasoning_content</code> and then output <code>tool_calls</code>. The two channels fire in sequence and are
still ultimately aggregated into the same <code>ChatResponse</code>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {...,"delta":{"reasoning_content":"用户问的是杭州天气，需要调用 get_weather。","content":null}...}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {...,"delta":{"reasoning_content":"参数 city=杭州。","content":null}...}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {...,"delta":{"tool_calls":[{"index":0,"id":"call_abc123","type":"function","function":{"name":"get_weather","arguments":"{\"city\":\"杭州\"}"}}]}...}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {...,"delta":{},"finish_reason":"tool_calls"}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: [DONE]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        │</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ▼ onCompleteResponse</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">ChatResponse{ thinking="用户问的是杭州天气，需要调用 get_weather。参数 city=杭州。",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">              toolExecutionRequests=[ToolExecutionRequest(id=call_abc123, name=get_weather, arguments={"city":"杭州"})],</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">              finishReason=TOOL_EXECUTION }</span><br></div></code></pre></div></div>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="62-anthropic">6.2 Anthropic<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#62-anthropic" class="hash-link" aria-label="Direct link to 6.2 Anthropic" title="Direct link to 6.2 Anthropic" translate="no">​</a></h3>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="621-non-streaming">6.2.1 Non-Streaming<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#621-non-streaming" class="hash-link" aria-label="Direct link to 6.2.1 Non-Streaming" title="Direct link to 6.2.1 Non-Streaming" translate="no">​</a></h4>
<p>curl:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> https://api.anthropic.com/v1/messages </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"x-api-key: </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$ANTHROPIC_API_KEY</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"anthropic-version: 2023-06-01"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Content-Type: application/json"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-d</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'{</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "model": "claude-3-5-sonnet-latest",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "max_tokens": 256,</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "system": "You are a helpful assistant.",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "messages": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      {"role": "user", "content": "用一句话解释什么是 CompletableFuture。"}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">  }'</span><br></div></code></pre></div></div>
<p>Return (excerpt):</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"id"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"msg_01AbCdEfGh"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"message"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"role"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"assistant"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"model"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"claude-3-5-sonnet-latest"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"content"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"text"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"text"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"CompletableFuture 是 Java 8 引入的异步编程工具，用来表示一个尚未完成、但未来会给出结果的计算。"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"stop_reason"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"end_turn"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"stop_sequence"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token null keyword" style="color:rgb(189, 147, 249);font-style:italic">null</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"usage"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"input_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">20</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"output_tokens"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">42</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>AgentForge-side mapping:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">content[].type == "text"  -&gt; concatenated into AiMessage.text()</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">stop_reason=end_turn      -&gt; FinishReason.STOP</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">usage.input_tokens        -&gt; TokenUsage.inputTokens  = 20</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">usage.output_tokens       -&gt; TokenUsage.outputTokens = 42</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">input + output            -&gt; TokenUsage.totalTokens  = 62</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">id / model / type         -&gt; ChatResponse.metadata</span><br></div></code></pre></div></div>
<p>Corresponding call:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">ChatModel</span><span class="token plain"> model </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">AnthropicChatModel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">apiKey</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getenv</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"ANTHROPIC_API_KEY"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">modelName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"claude-3-5-sonnet-latest"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">maxTokens</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">256</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">ChatResponse</span><span class="token plain"> response </span><span class="token operator">=</span><span class="token plain"> model</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">chat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">ChatRequest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">SystemMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"You are a helpful assistant."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">UserMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"用一句话解释什么是 CompletableFuture。"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">aiMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">text</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">tokenUsage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">totalTokens</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(98, 114, 164)">// 62</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Key point</strong>: <code>system</code> is <code>messages[].role=system</code> in OpenAI, and the <strong>top-level <code>system</code> field</strong> in Anthropic,
but the AgentForge upper layer always uses the same <code>SystemMessage</code>.</p>
</blockquote>
<br>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="622-streaming">6.2.2 Streaming<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#622-streaming" class="hash-link" aria-label="Direct link to 6.2.2 Streaming" title="Direct link to 6.2.2 Streaming" translate="no">​</a></h4>
<p>curl:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-N</span><span class="token plain"> https://api.anthropic.com/v1/messages </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"x-api-key: </span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$ANTHROPIC_API_KEY</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"anthropic-version: 2023-06-01"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-H</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Content-Type: application/json"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-d</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'{</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "model": "claude-3-5-sonnet-latest",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "max_tokens": 256,</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "stream": true,</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    "messages": [</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">      {"role": "user", "content": "用一句话解释什么是 CompletableFuture。"}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">    ]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">  }'</span><br></div></code></pre></div></div>
<p>Return (<code>event:</code> + <code>data:</code> frames):</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: message_start</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"message_start","message":{"id":"msg_01AbCdEfGh","type":"message","role":"assistant","model":"claude-3-5-sonnet-latest","content":[],"stop_reason":null,"usage":{"input_tokens":20,"output_tokens":1}}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: content_block_start</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: content_block_delta</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"CompletableFuture"}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: content_block_delta</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" 是 Java 8 引入的异步编程工具。"}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: content_block_stop</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"content_block_stop","index":0}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: message_delta</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":24}}</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">event: message_stop</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data: {"type":"message_stop"}</span><br></div></code></pre></div></div>
<p>AgentForge-side callback sequence:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">onPartialResponse("CompletableFuture")</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">onPartialResponse(" 是 Java 8 引入的异步编程工具。")</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">onCompleteResponse(ChatResponse{ text="CompletableFuture 是 Java 8 引入的异步编程工具。",</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                                finishReason=STOP, tokenUsage=(20,24) })</span><br></div></code></pre></div></div>
<p>The corresponding call is the same as 6.1.2, only replacing the model with <code>AnthropicStreamingChatModel</code>:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">StreamingChatModel</span><span class="token plain"> model </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">AnthropicStreamingChatModel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">apiKey</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getenv</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"ANTHROPIC_API_KEY"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">modelName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"claude-3-5-sonnet-latest"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">maxTokens</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">256</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">model</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">chat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"用一句话解释什么是 CompletableFuture。"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> handler</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Explanation of drawbacks</strong>: Anthropic streaming gives <code>usage</code> in two places — <code>message_start</code> gives
<code>input_tokens</code>, and <code>message_delta</code> gives the cumulative <code>output_tokens</code>; the Provider needs to merge the two
before writing <code>TokenUsage</code>.</p>
</blockquote>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="63-one-click-real-verification-scripts">6.3 One-Click Real Verification Scripts<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#63-one-click-real-verification-scripts" class="hash-link" aria-label="Direct link to 6.3 One-Click Real Verification Scripts" title="Direct link to 6.3 One-Click Real Verification Scripts" translate="no">​</a></h3>
<p>The repository already provides two runnable scripts:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">openai/verify-openai-chat.sh</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">anthropic/verify-anthropic-chat.sh</span><br></div></code></pre></div></div>
<p>Taking OpenAI as an example:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token builtin class-name" style="color:rgb(189, 147, 249)">export</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">OPENAI_API_KEY</span><span class="token operator">=</span><span class="token plain">sk-</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token plain">.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">bash</span><span class="token plain"> openai/verify-openai-chat.sh</span><br></div></code></pre></div></div>
<p>The script executes in sequence "non-streaming request -&gt; jq parse <code>tool_calls</code> -&gt; streaming request observe <code>data:</code>
frames"; the Anthropic script is similar. The real returns also follow the mapping rules of this chapter.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-verification-testing">7. Verification Testing<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#7-verification-testing" class="hash-link" aria-label="Direct link to 7. Verification Testing" title="Direct link to 7. Verification Testing" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="71-test-matrix">7.1 Test Matrix<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#71-test-matrix" class="hash-link" aria-label="Direct link to 7.1 Test Matrix" title="Direct link to 7.1 Test Matrix" translate="no">​</a></h3>
<table><thead><tr><th>Module</th><th>Test class</th><th>Coverage points</th></tr></thead><tbody><tr><td><code>agentforge-model-api</code></td><td><code>AiMessageToolCallTest</code></td><td>Single/multiple tool calls, text + tool call, thinking/attributes, value equality and immutability, <code>UserMessage</code> name + multiple <code>Content</code>, <code>ToolExecutionRequest</code> field round-trip</td></tr><tr><td><code>agentforge-model-api</code></td><td><code>ChatModelTest</code> / <code>StreamingChatModelTest</code></td><td>Convenience method convergence, streaming lifecycle</td></tr><tr><td><code>agentforge-model-core</code></td><td><code>DefaultChatRequestParametersTest</code></td><td>Default values / merge overrides / customParameters Map merge</td></tr><tr><td><code>agentforge-model-core</code></td><td><code>ToolServiceTest</code> / <code>DefaultToolExecutorTest</code></td><td>Reasoning-execution loop, parameter binding, error handling</td></tr><tr><td><code>agentforge-model-openai</code></td><td><code>OpenAiFunctionCallTest</code></td><td>Single/multiple <code>tool_calls</code>, mixed text, <code>tools</code>/<code>tool_choice</code> (including SPECIFIC) serialization, tool result flow-back</td></tr><tr><td><code>agentforge-model-openai</code></td><td><code>OpenAiStreamingFunctionCallTest</code></td><td>Single tool call delta merge, multiple tools interleaved, text + tool in the same stream, no-<code>index</code> fallback</td></tr><tr><td><code>agentforge-model-anthropic</code></td><td><code>AnthropicToolUseTest</code></td><td><code>tool_use</code> parsing, multiple <code>tool_use</code>, <code>tool_result</code> flow-back, <code>input_schema</code> serialization, multi-<code>Content</code> user message</td></tr><tr><td><code>agentforge-model-anthropic</code></td><td><code>AnthropicStreamingToolUseTest</code></td><td><code>input_json_delta</code> aggregation, mixed streaming of <code>text_delta</code> and multiple <code>tool_use</code>, metadata / token usage</td></tr></tbody></table>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="72-how-to-avoid-depending-on-a-real-api-key">7.2 How to Avoid Depending on a Real API Key<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#72-how-to-avoid-depending-on-a-real-api-key" class="hash-link" aria-label="Direct link to 7.2 How to Avoid Depending on a Real API Key" title="Direct link to 7.2 How to Avoid Depending on a Real API Key" translate="no">​</a></h3>
<p><strong>Question prompt</strong>: How do 140+ unit tests run stably without a real API Key?</p>
<p>The answer is the <code>HttpTransport</code> SPI. During testing, inject a Fake Transport that directly returns preset
JSON / SSE lines, which can cover:</p>
<ul>
<li class="">Whether the request payload serialization is correct;</li>
<li class="">Whether the response parsing, finish reason, and token usage mapping are correct;</li>
<li class="">Whether the streaming aggregation and tool call merging are correct;</li>
<li class="">Whether the error path (HTTP 4xx/5xx) is converted into <code>ModelException</code>.</li>
</ul>
<blockquote>
<p><strong>Key point</strong>: The replaceability of the transport layer is not only for supporting OkHttp, but also for making the
core protocol logic offline and deterministically unit-testable.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-boundaries-compatibility-and-evolution">8. Boundaries, Compatibility, and Evolution<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#8-boundaries-compatibility-and-evolution" class="hash-link" aria-label="Direct link to 8. Boundaries, Compatibility, and Evolution" title="Direct link to 8. Boundaries, Compatibility, and Evolution" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="81-capabilities-already-delivered">8.1 Capabilities Already Delivered<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#81-capabilities-already-delivered" class="hash-link" aria-label="Direct link to 8.1 Capabilities Already Delivered" title="Direct link to 8.1 Capabilities Already Delivered" translate="no">​</a></h3>
<ul>
<li class=""><code>ChatModel</code> / <code>StreamingChatModel</code> unified entry;</li>
<li class=""><code>ChatRequest</code> / <code>ChatRequestParameters</code> / <code>ChatResponse</code> unified input/output;</li>
<li class=""><code>AiMessage.toolExecutionRequests()</code> + <code>ToolExecutionRequest</code>;</li>
<li class=""><code>ToolSpecification</code> / <code>ToolParameters</code> (JSON-Schema style, pure <code>Map</code>) / <code>ToolChoice</code>;</li>
<li class="">The tool request sending chain and tool-result wire mapping for OpenAI / Anthropic;</li>
<li class="">OpenAI streaming <code>tool_calls</code> delta aggregated by <code>index</code>;</li>
<li class=""><code>AnthropicStreamingChatModel</code> (including <code>tool_use</code> <code>input_json_delta</code> aggregation);</li>
<li class=""><code>AiMessage.thinking()</code> / <code>attributes</code> and <code>StreamingChatResponseHandler.onPartialThinking(...)</code>;</li>
<li class="">Multimodal modeling of <code>UserMessage</code> with <code>name</code> + <code>List&lt;Content&gt;</code> (currently provides <code>TextContent</code>);</li>
<li class=""><code>agentforge-model-registry</code> factory-style assembly.</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="82-capabilities-still-to-be-completed">8.2 Capabilities Still To Be Completed<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#82-capabilities-still-to-be-completed" class="hash-link" aria-label="Direct link to 8.2 Capabilities Still To Be Completed" title="Direct link to 8.2 Capabilities Still To Be Completed" translate="no">​</a></h3>
<ul>
<li class="">Multimodal <code>Content</code> implementations such as <code>ImageContent</code> / <code>AudioContent</code> / <code>VideoContent</code> / <code>PdfFileContent</code> and Provider wire mapping;</li>
<li class="">Exposing an <code>onPartialToolCall(...)</code> incremental callback during streaming (currently only the final <code>ChatResponse</code> exposes the complete tool call);</li>
<li class="">Structured Output / JSON Schema strongly-typed parameter binding and runtime validation;</li>
<li class="">Provider-level strongly-typed protocol for <code>thinking</code> (currently only the field position is reserved);</li>
<li class="">Prompt Cache;</li>
<li class="">retry / backoff / rate-limit policy;</li>
<li class="">metrics / tracing / request-id standardization;</li>
<li class="">Other model categories: <code>EmbeddingModel</code> / <code>ImageModel</code> / <code>AudioModel</code>.</li>
</ul>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="83-the-boundary-of-customparameters">8.3 The Boundary of customParameters<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#83-the-boundary-of-customparameters" class="hash-link" aria-label="Direct link to 8.3 The Boundary of customParameters" title="Direct link to 8.3 The Boundary of customParameters" translate="no">​</a></h3>
<p><code>customParameters</code> is used to quickly support new Provider fields:</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token class-name">DefaultChatRequestParameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">customParameter</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"reasoning_effort"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"high"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<blockquote>
<p><strong>Explanation of drawbacks</strong>: If <code>customParameters</code> is abused, the Core type system will gradually become
ineffective, and the upper layer will again be forced to be aware of vendor fields. Therefore it should only be
a "temporary channel", not a "long-term solution".</p>
</blockquote>
<p>Judgment criteria:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Used by only a single Provider             -&gt; customParameters</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Appears in multiple Providers and has stable semantics -&gt; Core strongly-typed parameter</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Must be understood by the core Agent/Tool flow        -&gt; Core strongly-typed model</span><br></div></code></pre></div></div>
<br>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="84-compatibility-principles">8.4 Compatibility Principles<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#84-compatibility-principles" class="hash-link" aria-label="Direct link to 8.4 Compatibility Principles" title="Direct link to 8.4 Compatibility Principles" translate="no">​</a></h3>
<ol>
<li class="">Prefer adding interface default methods or new types, avoiding breaking existing Providers;</li>
<li class="">Core does not expose vendor SDK types;</li>
<li class="">Provider-specific protocols should preferably stay in the Provider module;</li>
<li class="">request/response objects should be as immutable as possible;</li>
<li class="">The public API remains compilable on Java 8, and running on JDK 17 is recommended by default;</li>
<li class="">Streaming and Blocking must ultimately converge to the same <code>ChatResponse</code> semantics.</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="9-summary">9. Summary<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#9-summary" class="hash-link" aria-label="Direct link to 9. Summary" title="Direct link to 9. Summary" translate="no">​</a></h2>
<p>Returning to the question at the beginning: <strong>When swapping models, can the Agent main loop remain unchanged with not a single line modified?</strong></p>
<p>AgentForge's answer is: yes. The approach is to thoroughly separate "what changes" from "what does not change" —</p>
<ul>
<li class=""><strong>What does not change</strong>: the semantics of <code>ChatModel</code> / <code>ChatRequest</code> / <code>ChatMessage</code> / <code>ChatResponse</code>, the streaming lifecycle, the exception boundary;</li>
<li class=""><strong>What changes</strong>: each vendor's wire protocol, all pushed down into their respective Provider modules, and isolated and assembled with the help of <code>HttpTransport</code>, package-private wire helpers, and <code>LlmFactory</code>.</li>
</ul>
<p>This forms a replicable extension path: <strong>define the standard protocol → add a Provider module → five-step protocol conversion → register into LlmFactory</strong>.
This set of boundaries ensures that as AgentForge continues to evolve from the LLM layer toward Tool Calling, Agent Runtime, Memory, and Workflow, the upper layer will not be locked in by any single model protocol, and it also reduces "adding one more protocol model" to a low-risk, replicable task.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://agentforge-tech.github.io/AgentForge/en/blog/01%E3%80%81model%E6%A8%A1%E5%9E%8B%E5%8D%8F%E8%AE%AE%E5%B1%82/chatmodel/%E7%BB%9F%E4%B8%80ChatModel%E5%8D%8F%E8%AE%AE%E5%B1%8201%E3%80%81ChatModel%20%E6%A0%B8%E5%BF%83%E5%8D%8F%E8%AE%AE%E5%B1%82%E8%AE%BE%E8%AE%A1%EF%BC%9A%E7%BB%9F%E4%B8%80%20API%20%E5%A5%91%E7%BA%A6%E5%B0%81%E8%A3%85%E4%B8%8E%E5%A4%9A%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95%E5%AE%9E%E7%8E%B0#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<p>[1]. <a href="https://github.com/langchain4j/langchain4j" target="_blank" rel="noopener noreferrer" class="">LangChain4j - Build LLM-powered applications in Java</a></p>
<p>[2]. <a href="https://platform.openai.com/docs/api-reference/chat" target="_blank" rel="noopener noreferrer" class="">OpenAI Chat Completions API Reference</a></p>
<p>[3]. <a href="https://docs.anthropic.com/en/api/messages" target="_blank" rel="noopener noreferrer" class="">Anthropic Messages API Reference</a></p>
<p>[4]. <a href="https://platform.openai.com/docs/api-reference/chat/streaming" target="_blank" rel="noopener noreferrer" class="">OpenAI Streaming Responses (SSE)</a></p>
<p>[5]. <a href="https://docs.anthropic.com/en/api/messages-streaming" target="_blank" rel="noopener noreferrer" class="">Anthropic Streaming Messages</a></p>
<p>Compiled by: Changlu Created: 2026.9.13 Updated: 2026.10.6</p>]]></content:encoded>
            <category>AgentForge</category>
            <category>Model Protocol Layer</category>
            <category>ChatModel</category>
        </item>
    </channel>
</rss>