summaryrefslogtreecommitdiff
path: root/third_party/mcpc
diff options
context:
space:
mode:
authortanaka takashi <isazaka@gmail.com>2026-06-30 19:03:25 +0900
committertanaka takashi <isazaka@gmail.com>2026-06-30 19:03:25 +0900
commit05c8e0a31a0d4192c590e833b24393208e1c751f (patch)
treef7c2c82c9327abde67bf3acf1d0258573b3bc6c2 /third_party/mcpc
parent4bc721c3bf7589b519c5730d093f704ce56e3f09 (diff)
fix: MCPCをvendored fork化しMCP準拠と接続安定性を改善
- Git submoduleを廃止してMCPCを直接管理 - Windowsで254リクエスト付近に発生する切断を修正 - JSON-RPC 2.0エラー応答を標準化 - tool schemaとruntimeの必須・任意引数を一致 - 1000回連続呼び出しの回帰テストを追加
Diffstat (limited to 'third_party/mcpc')
m---------third_party/mcpc0
-rw-r--r--third_party/mcpc/.github/workflows/mcpc.yml112
-rw-r--r--third_party/mcpc/.gitignore30
-rw-r--r--third_party/mcpc/.indent.pro1
-rw-r--r--third_party/mcpc/CMakeLists.txt10
-rw-r--r--third_party/mcpc/Makefile51
-rw-r--r--third_party/mcpc/README.md474
-rw-r--r--third_party/mcpc/README.org197
-rw-r--r--third_party/mcpc/UPSTREAM.md12
-rw-r--r--third_party/mcpc/licenses/LICENSE-mjson.txt21
-rw-r--r--third_party/mcpc/mcpc/_c23_keywords.h30
-rw-r--r--third_party/mcpc/mcpc/_c23_stdckdint.h28
-rw-r--r--third_party/mcpc/mcpc/_c23_uchar.h31
-rw-r--r--third_party/mcpc/mcpc/anydata.h101
-rw-r--r--third_party/mcpc/mcpc/errcode.h50
-rw-r--r--third_party/mcpc/mcpc/mcpc.h33
-rw-r--r--third_party/mcpc/mcpc/prmpt.h98
-rw-r--r--third_party/mcpc/mcpc/rsc.h60
-rw-r--r--third_party/mcpc/mcpc/server.h89
-rw-r--r--third_party/mcpc/mcpc/tool.h85
-rw-r--r--third_party/mcpc/mcpc/ucbr.h55
-rw-r--r--third_party/mcpc/mcpc/visb.h38
-rw-r--r--third_party/mcpc/misc/api.md1112
-rw-r--r--third_party/mcpc/misc/api.org1193
-rw-r--r--third_party/mcpc/misc/init-msvc.bat5
-rw-r--r--third_party/mcpc/mk/ccdefs-cygwin.mk34
-rw-r--r--third_party/mcpc/mk/ccdefs-linux.mk79
-rw-r--r--third_party/mcpc/mk/ccdefs-macos.mk53
-rw-r--r--third_party/mcpc/mk/ccdefs-mingw.mk89
-rw-r--r--third_party/mcpc/mk/ccdefs-win.mk60
-rw-r--r--third_party/mcpc/mk/dbgrel.mk92
-rw-r--r--third_party/mcpc/mk/step0.mk31
-rw-r--r--third_party/mcpc/src/.indent.pro1
-rw-r--r--third_party/mcpc/src/CMakeLists.txt32
-rw-r--r--third_party/mcpc/src/Makefile94
-rw-r--r--third_party/mcpc/src/alloc.c57
-rw-r--r--third_party/mcpc/src/alloc.h39
-rw-r--r--third_party/mcpc/src/anydata.c817
-rw-r--r--third_party/mcpc/src/anydata.h225
-rw-r--r--third_party/mcpc/src/complt.c162
-rw-r--r--third_party/mcpc/src/complt.h60
-rw-r--r--third_party/mcpc/src/errcode.c39
-rw-r--r--third_party/mcpc/src/errcode.h74
-rw-r--r--third_party/mcpc/src/ext_stdint.h54
-rw-r--r--third_party/mcpc/src/ext_string.h75
-rw-r--r--third_party/mcpc/src/log.c46
-rw-r--r--third_party/mcpc/src/log.h60
-rw-r--r--third_party/mcpc/src/mcpc.pc10
-rw-r--r--third_party/mcpc/src/mjson.c1372
-rw-r--r--third_party/mcpc/src/mjson.h296
-rw-r--r--third_party/mcpc/src/prmpt.c378
-rw-r--r--third_party/mcpc/src/prmpt.h80
-rw-r--r--third_party/mcpc/src/retbuf.c119
-rw-r--r--third_party/mcpc/src/retbuf.h50
-rw-r--r--third_party/mcpc/src/rsc.c177
-rw-r--r--third_party/mcpc/src/rsc.h51
-rw-r--r--third_party/mcpc/src/serlz.c619
-rw-r--r--third_party/mcpc/src/serlz.h62
-rw-r--r--third_party/mcpc/src/server.c1830
-rw-r--r--third_party/mcpc/src/server.h196
-rw-r--r--third_party/mcpc/src/tool.c505
-rw-r--r--third_party/mcpc/src/tool.h150
-rw-r--r--third_party/mcpc/src/ucbr.c288
-rw-r--r--third_party/mcpc/src/ucbr.h61
-rw-r--r--third_party/mcpc/src/ver.txt1
-rw-r--r--third_party/mcpc/tst/.indent.pro1
-rw-r--r--third_party/mcpc/tst/CMakeLists.txt33
-rw-r--r--third_party/mcpc/tst/Makefile108
-rw-r--r--third_party/mcpc/tst/commtst.c181
-rw-r--r--third_party/mcpc/tst/commtst.h147
-rw-r--r--third_party/mcpc/tst/itst-svcalc-req-promptslist-1.json1
-rw-r--r--third_party/mcpc/tst/itst-svcalc-req-resourceslist-1.json1
-rw-r--r--third_party/mcpc/tst/itst-svcalc-req-toolslist-1.json1
-rw-r--r--third_party/mcpc/tst/itst-svcalc.c207
74 files changed, 13114 insertions, 0 deletions
diff --git a/third_party/mcpc b/third_party/mcpc
deleted file mode 160000
-Subproject 4025fddfedcfa7573803a47236b147733fcb3c0
diff --git a/third_party/mcpc/.github/workflows/mcpc.yml b/third_party/mcpc/.github/workflows/mcpc.yml
new file mode 100644
index 0000000..832395b
--- /dev/null
+++ b/third_party/mcpc/.github/workflows/mcpc.yml
@@ -0,0 +1,112 @@
+name: mcpc
+run-name: mcpc
+on: [push]
+jobs:
+ x64-linux-gcc10-glibc231:
+ if: contains(github.event.head_commit.message, 'NO_GH_CI') == false
+ runs-on: ubuntu-24.04
+ steps:
+ - uses: actions/checkout@v4
+ - run: ls -l .
+ - run: sudo apt-get install -y buildah podman
+ - run: buildah from --name wctn docker.io/library/debian:bullseye
+ - run: buildah run wctn -- apt-get update
+ - run: buildah run wctn -- apt-get install -y sudo bash gcc make
+ - run: buildah copy wctn ../mcpc /mcpc
+ - run: buildah run wctn -- bash -c "cd /mcpc && CC=gcc make tst"
+ - run: buildah run wctn -- bash -c "cd /mcpc && CC=gcc make install"
+ - run: buildah run wctn -- bash -c "ls -l /usr/local/lib"
+ x64-linux-gcc12-glibc236:
+ if: contains(github.event.head_commit.message, 'NO_GH_CI') == false
+ runs-on: ubuntu-24.04
+ steps:
+ - uses: actions/checkout@v4
+ - run: ls -l .
+ - run: sudo apt-get install -y buildah podman
+ - run: buildah from --name wctn docker.io/library/debian:bookworm
+ - run: buildah run wctn -- apt-get update
+ - run: buildah run wctn -- apt-get install -y sudo bash gcc make
+ - run: buildah copy wctn ../mcpc /mcpc
+ - run: buildah run wctn -- bash -c "cd /mcpc && CC=gcc make tst"
+ - run: buildah run wctn -- bash -c "cd /mcpc && CC=gcc make install"
+ - run: buildah run wctn -- bash -c "ls -l /usr/local/lib"
+ x64-linux-gcc14-glibc240:
+ if: contains(github.event.head_commit.message, 'NO_GH_CI') == false
+ runs-on: ubuntu-24.04
+ steps:
+ - uses: actions/checkout@v4
+ - run: ls -l .
+ - run: sudo apt-get install -y buildah podman
+ - run: buildah from --name wctn docker.io/library/fedora:41
+ - run: buildah run wctn -- dnf install -y bash gcc make
+ - run: buildah copy wctn ../mcpc /mcpc
+ - run: buildah run wctn -- bash -c "cd /mcpc && CC=gcc make tst"
+ - run: buildah run wctn -- bash -c "cd /mcpc && CC=gcc make install"
+ - run: buildah run wctn -- bash -c "ls -l /usr/local/lib"
+ x64-win2022-vs2022:
+ if: contains(github.event.head_commit.message, 'NO_GH_CI') == false
+ runs-on: windows-2022
+ steps:
+ - uses: actions/checkout@v4
+ - shell: cmd
+ run: ${{ '"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" && dir' }}
+ - shell: cmd
+ run: ${{ '"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" && make' }}
+ - shell: cmd
+ run: ${{ '"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" && make tst' }}
+ - run: dir tst/itst-svcalc.exe
+ - run: ldd tst/itst-svcalc.exe
+ x64-win2025-vs2022:
+ if: contains(github.event.head_commit.message, 'NO_GH_CI') == false
+ runs-on: windows-2025
+ steps:
+ - uses: actions/checkout@v4
+ - shell: cmd
+ run: ${{ '"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" && dir' }}
+ - shell: cmd
+ run: ${{ '"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" && make' }}
+ - shell: cmd
+ run: ${{ '"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" && make tst' }}
+ - run: dir tst/itst-svcalc.exe
+ - run: ldd tst/itst-svcalc.exe
+ x64-win2022-gcc:
+ if: contains(github.event.head_commit.message, 'NO_GH_CI') == false
+ runs-on: windows-2022
+ steps:
+ - uses: actions/checkout@v4
+ - run: gcc --version
+ - run: make --version
+ - run: make tst
+ - run: ldd tst/itst-svcalc.out
+ x64-win2025-gcc:
+ if: contains(github.event.head_commit.message, 'NO_GH_CI') == false
+ runs-on: windows-2025
+ steps:
+ - uses: actions/checkout@v4
+ - run: gcc --version
+ - run: make --version
+ - run: make tst
+ - run: ldd tst/itst-svcalc.out
+ x64-mac13-clang:
+ if: contains(github.event.head_commit.message, 'NO_GH_CI') == false
+ runs-on: macos-13
+ steps:
+ - uses: actions/checkout@v4
+ - run: make tst
+ - run: otool -L tst/itst-svcalc.out
+ x64-mac14-clang:
+ if: contains(github.event.head_commit.message, 'NO_GH_CI') == false
+ runs-on: macos-14
+ steps:
+ - uses: actions/checkout@v4
+ - run: make tst
+ - run: otool -L tst/itst-svcalc.out
+ x64-mac15-clang:
+ if: contains(github.event.head_commit.message, 'NO_GH_CI') == false
+ runs-on: macos-15
+ steps:
+ - uses: actions/checkout@v4
+ - run: make tst
+ - run: otool -L tst/itst-svcalc.out
+
+
diff --git a/third_party/mcpc/.gitignore b/third_party/mcpc/.gitignore
new file mode 100644
index 0000000..cc32dfd
--- /dev/null
+++ b/third_party/mcpc/.gitignore
@@ -0,0 +1,30 @@
+*
+!.gitignore
+!**/
+
+!.github/workflows/*.yml
+
+!*.org
+!*.md
+
+!ver.txt
+!mcpc.pc
+!*.patch
+!*.c
+!*.h
+!*.py
+!*.bash
+!*.bat
+!*.mk
+!CMakeLists.txt
+!*help.txt
+!*.json
+!LICENSE*.txt
+
+!Makefile
+!.indent.pro
+
+!/AUTHORS
+!/ChangeLog
+!/COPYING
+!/README
diff --git a/third_party/mcpc/.indent.pro b/third_party/mcpc/.indent.pro
new file mode 100644
index 0000000..53ac612
--- /dev/null
+++ b/third_party/mcpc/.indent.pro
@@ -0,0 +1 @@
+--line-length120 \ No newline at end of file
diff --git a/third_party/mcpc/CMakeLists.txt b/third_party/mcpc/CMakeLists.txt
new file mode 100644
index 0000000..e3a330d
--- /dev/null
+++ b/third_party/mcpc/CMakeLists.txt
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
+
+project(mcpc)
+
+add_subdirectory(src)
+
+enable_testing()
+include(CTest)
+add_subdirectory(tst)
+
diff --git a/third_party/mcpc/Makefile b/third_party/mcpc/Makefile
new file mode 100644
index 0000000..d01f119
--- /dev/null
+++ b/third_party/mcpc/Makefile
@@ -0,0 +1,51 @@
+include mk/step0.mk
+
+subdirs =
+subdirs += src
+subdirs2 =
+subdirs2 += tst
+
+default: link
+
+clean:
+ $(if $(is_sh),@for el in $(subdirs); do $(MAKE) -C $$el $@; done)
+ $(if $(is_sh),@for el in $(subdirs2); do $(MAKE) -C $$el $@; done)
+ $(if $(is_cmd),@for %%e in ($(subdirs)) do $(MAKE) -C %%e -f $(firstword $(MAKEFILE_LIST)) $@)
+ $(if $(is_cmd),@for %%e in ($(subdirs2)) do $(MAKE) -C %%e -f $(firstword $(MAKEFILE_LIST)) $@)
+
+link:
+ $(if $(is_sh),@for el in $(subdirs); do $(MAKE) -C $$el $@; done)
+ $(if $(is_cmd),@for %%e in ($(subdirs)) do $(MAKE) -C %%e -f $(firstword $(MAKEFILE_LIST)) $@)
+
+tstcomp: link
+ $(if $(is_sh),@for el in $(subdirs2); do $(MAKE) -C $$el comp; done)
+ $(if $(is_cmd),@for %%e in ($(subdirs2)) do $(MAKE) -C %%e -f $(firstword $(MAKEFILE_LIST)) comp)
+
+tstlink: link
+ $(if $(is_sh),@for el in $(subdirs2); do $(MAKE) -C $$el link; done)
+ $(if $(is_cmd),@for %%e in ($(subdirs2)) do $(MAKE) -C %%e -f $(firstword $(MAKEFILE_LIST)) link)
+
+utst: link
+ $(if $(is_sh),@for el in $(subdirs2); do $(MAKE) -C $$el $@; done)
+ $(if $(is_cmd),@for %%e in ($(subdirs2)) do $(MAKE) -C %%e -f $(firstword $(MAKEFILE_LIST)) $@)
+
+itst: link
+ $(if $(is_sh),@for el in $(subdirs2); do $(MAKE) -C $$el $@; done)
+ $(if $(is_cmd),@for %%e in ($(subdirs2)) do $(MAKE) -C %%e -f $(firstword $(MAKEFILE_LIST)) $@)
+
+tst: utst itst
+
+PREFIX ?= /usr/local
+INC_DIR = $(PREFIX)/include
+LIB_DIR = $(PREFIX)/lib
+PC_DIR = $(LIB_DIR)/pkgconfig
+VER = $(shell cat src/ver.txt)
+
+install: tst
+ $(if $(is_sh), install -d $(INC_DIR)/mcpc $(LIB_DIR) $(PC_DIR) )
+ $(if $(is_sh), install -m 644 mcpc/* $(INC_DIR)/mcpc )
+ $(if $(is_sh), install -m 644 src/libmcpc.a $(LIB_DIR) )
+ $(if $(is_sh), install -m 755 src/libmcpc.so $(LIB_DIR)/libmcpc.so.$(VER) )
+ $(if $(is_sh), install -m 644 src/mcpc.pc $(PC_DIR) )
+ $(if $(is_sh), ln -sf $(LIB_DIR)/libmcpc.so.$(VER) $(LIB_DIR)/libmcpc.so )
+ $(if $(is_cmd), echo Automatic installation not supported on this platform )
diff --git a/third_party/mcpc/README.md b/third_party/mcpc/README.md
new file mode 100644
index 0000000..9466498
--- /dev/null
+++ b/third_party/mcpc/README.md
@@ -0,0 +1,474 @@
+
+# Table of Contents
+
+- [Overview](#overview)
+- [MCP Features](#features)
+- [Supported Platforms](#platforms)
+- [Modern C Support](#modernc)
+- [Building](#building)
+- [Development](#development)
+- [FAQ](#org73da0a6)
+- [License](#license)
+
+
+
+<a id="overview"></a>
+
+# Overview
+
+mcpc is a <b>M</b>odel <b>C</b>ontext <b>P</b>rotocol library in modern <b>C</b>. Its major goals are:
+
+- Provide the **most native** bridge between low-level infrastructure
+ and AI models
+- Explore the maximum capability of the **most recent** C programming language
+
+
+<a id="features"></a>
+
+# MCP Features
+
+<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
+
+
+<colgroup>
+<col class="org-left" />
+
+<col class="org-left" />
+
+<col class="org-left" />
+</colgroup>
+<thead>
+<tr>
+<th scope="col" class="org-left">Features</th>
+<th scope="col" class="org-left">Server</th>
+<th scope="col" class="org-left">Client</th>
+</tr>
+</thead>
+
+<tbody>
+<tr>
+<td class="org-left">Tool</td>
+<td class="org-left">✅</td>
+<td class="org-left">🔨</td>
+</tr>
+
+
+<tr>
+<td class="org-left">Resource</td>
+<td class="org-left">✅</td>
+<td class="org-left">🔨</td>
+</tr>
+
+
+<tr>
+<td class="org-left">Prompt</td>
+<td class="org-left">✅</td>
+<td class="org-left">🔨</td>
+</tr>
+
+
+<tr>
+<td class="org-left">Completion</td>
+<td class="org-left">✅</td>
+<td class="org-left">🔨</td>
+</tr>
+
+
+<tr>
+<td class="org-left">STDIO Transport</td>
+<td class="org-left">✅</td>
+<td class="org-left">🔨</td>
+</tr>
+
+
+<tr>
+<td class="org-left">HTTP Transport</td>
+<td class="org-left">🔨</td>
+<td class="org-left">🔨</td>
+</tr>
+
+
+<tr>
+<td class="org-left">Others</td>
+<td class="org-left">🔨</td>
+<td class="org-left">🔨</td>
+</tr>
+</tbody>
+</table>
+
+
+<a id="platforms"></a>
+
+# Supported Platforms
+
+<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
+
+
+<colgroup>
+<col class="org-left" />
+
+<col class="org-left" />
+
+<col class="org-left" />
+
+<col class="org-left" />
+</colgroup>
+<thead>
+<tr>
+<th scope="col" class="org-left">&#xa0;</th>
+<th scope="col" class="org-left">Linux</th>
+<th scope="col" class="org-left">Windows</th>
+<th scope="col" class="org-left">macOS</th>
+</tr>
+</thead>
+
+<tbody>
+<tr>
+<td class="org-left">Linux</td>
+<td class="org-left">✅</td>
+<td class="org-left">✅</td>
+<td class="org-left">✅</td>
+</tr>
+</tbody>
+</table>
+
+Note: this table roughly shows the state of supporting on some platforms,
+for more detailed information, check the *Modern C Support*.
+
+
+<a id="modernc"></a>
+
+# Modern C Support
+
+Since mcpc claims to implement everything with the most modern C
+possible, its building process becomes a little different than many
+existing building systems or process, the major
+difference is that: ****we try to support as many C compilers as possible,
+even on one platform****.
+
+Ideally mcpc will be implemented fully in C23(currently the newest
+standard), but if a full C23 is missing on a platform, we
+either downgrade the C standard, or give up the support.
+
+This approach certainly has some benefits and consequences:
+
+1. The library can launch on as many platforms as possible, and be
+ able to compiled by as many C compilers as possible.
+2. There must be a minimum fallback version of C standard that the
+ library targets, currently it is C11.
+3. C23 features that are not patchable, or require non-trivial amount
+ of effort to patch, will not be used, e.g., [N2508](https://open-std.org/JTC1/SC22/WG14/www/docs/n2508.pdf), [N2645](https://open-std.org/JTC1/SC22/WG14/www/docs/n2645.pdf), etc.
+4. All safety checks and performance optimization will only focus on the
+ platforms with full C23 support.
+
+
+## Support Matrix
+
+`✅✅` : *implemented in C23*
+
+`🔨✅` : *implemented in C11~C17*
+
+`-` : *the platform does not exist, or work in progress*
+
+Linux:
+
+<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
+
+
+<colgroup>
+<col class="org-left" />
+
+<col class="org-left" />
+
+<col class="org-left" />
+
+<col class="org-left" />
+</colgroup>
+<thead>
+<tr>
+<th scope="col" class="org-left">&#xa0;</th>
+<th scope="col" class="org-left">GLIBC 2.31</th>
+<th scope="col" class="org-left">GLIBC 2.36</th>
+<th scope="col" class="org-left">GLIBC 2.40</th>
+</tr>
+</thead>
+
+<tbody>
+<tr>
+<td class="org-left">GCC 10</td>
+<td class="org-left">🔨✅ (Debian 11)</td>
+<td class="org-left">-</td>
+<td class="org-left">-</td>
+</tr>
+
+
+<tr>
+<td class="org-left">GCC 12</td>
+<td class="org-left">-</td>
+<td class="org-left">🔨✅ (Debian 12)</td>
+<td class="org-left">-</td>
+</tr>
+
+
+<tr>
+<td class="org-left">GCC 13</td>
+<td class="org-left">-</td>
+<td class="org-left">-</td>
+<td class="org-left">-</td>
+</tr>
+
+
+<tr>
+<td class="org-left">GCC 14</td>
+<td class="org-left">-</td>
+<td class="org-left">-</td>
+<td class="org-left">✅✅ (Fedora 41)</td>
+</tr>
+
+
+<tr>
+<td class="org-left">Clang 14</td>
+<td class="org-left">-</td>
+<td class="org-left">-</td>
+<td class="org-left">-</td>
+</tr>
+
+
+<tr>
+<td class="org-left">Clang 16</td>
+<td class="org-left">-</td>
+<td class="org-left">-</td>
+<td class="org-left">-</td>
+</tr>
+
+
+<tr>
+<td class="org-left">Clang 18</td>
+<td class="org-left">-</td>
+<td class="org-left">-</td>
+<td class="org-left">-</td>
+</tr>
+
+
+<tr>
+<td class="org-left">Clang 19</td>
+<td class="org-left">-</td>
+<td class="org-left">-</td>
+<td class="org-left">✅✅ (Fedora 41)</td>
+</tr>
+</tbody>
+</table>
+
+Windows:
+
+<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
+
+
+<colgroup>
+<col class="org-left" />
+
+<col class="org-left" />
+
+<col class="org-left" />
+
+<col class="org-left" />
+</colgroup>
+<thead>
+<tr>
+<th scope="col" class="org-left">&#xa0;</th>
+<th scope="col" class="org-left">CMD/PS</th>
+<th scope="col" class="org-left">Cygwin</th>
+<th scope="col" class="org-left">MinGW</th>
+</tr>
+</thead>
+
+<tbody>
+<tr>
+<td class="org-left">MSVC 19</td>
+<td class="org-left">🔨✅</td>
+<td class="org-left">-</td>
+<td class="org-left">-</td>
+</tr>
+
+
+<tr>
+<td class="org-left">Clang 19</td>
+<td class="org-left">-</td>
+<td class="org-left">-</td>
+<td class="org-left">-</td>
+</tr>
+
+
+<tr>
+<td class="org-left">GCC 14</td>
+<td class="org-left">-</td>
+<td class="org-left">✅✅</td>
+<td class="org-left">✅✅</td>
+</tr>
+</tbody>
+</table>
+
+macOS:
+
+<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
+
+
+<colgroup>
+<col class="org-left" />
+
+<col class="org-left" />
+
+<col class="org-left" />
+
+<col class="org-left" />
+</colgroup>
+<thead>
+<tr>
+<th scope="col" class="org-left">&#xa0;</th>
+<th scope="col" class="org-left">macOS 13</th>
+<th scope="col" class="org-left">macOS 14</th>
+<th scope="col" class="org-left">macOS 15</th>
+</tr>
+</thead>
+
+<tbody>
+<tr>
+<td class="org-left">Clang 14</td>
+<td class="org-left">🔨✅</td>
+<td class="org-left">-</td>
+<td class="org-left">-</td>
+</tr>
+
+
+<tr>
+<td class="org-left">Clang 16</td>
+<td class="org-left">-</td>
+<td class="org-left">🔨✅</td>
+<td class="org-left">🔨✅</td>
+</tr>
+
+
+<tr>
+<td class="org-left">GCC 14</td>
+<td class="org-left">🔨✅</td>
+<td class="org-left">-</td>
+<td class="org-left">-</td>
+</tr>
+</tbody>
+</table>
+
+
+<a id="building"></a>
+
+# Building
+
+
+## Building (Linux, Cygwin, MSYS2, macOS, etc.)
+
+Prerequisites:
+
+- GNU Make
+
+Then
+
+ cd mcpc
+ make && make tst && make install
+
+
+## Building (Windows CMD/PS/VS)
+
+Prerequisites:
+
+- Visual Studio
+- GNU Make (`winget install ezwinports.make`)
+
+Two options to build:
+
+
+### 1. Build in CMD/PS
+
+Open "x64 Native Tools Command Prompt", then
+
+ cd mcpc
+ make && make tst
+
+
+### 2. Build in Visual Studio:
+
+"File" - "Open" - "CMake", "Build" - "Build All"
+
+> This approach is only for the better debugging experience on Windows,
+> *"Build in CMD/PS"* takes priority over this one.
+
+
+<a id="development"></a>
+
+# Development
+
+API reference can be found at [api.md](./misc/api.md).
+
+Dependencies:
+
+- [mjson](https://github.com/cesanta/mjson) (already in-tree)
+
+
+## Code Contributing
+
+Despite we claim to use modern C, as standard as possible, and as
+modern as possible, we have some extra conventions, in order to render
+our code more readable, inclusive and scalable:
+
+1. Unless mentioned explicitly, we follow GNU C [coding style](https://www.gnu.org/prep/standards/html_node/Writing-C.html).
+2. Prefer functions over macros, use macros only if necessary.
+3. The type of variables should be reflected in the source code. Use of
+ keyword `auto` is thus forbidden.
+4. Names of defined variables that are exposed publicly should be in
+ uppercase, otherwise in lowercase.
+
+
+<a id="org73da0a6"></a>
+
+# FAQ
+
+
+## Should I prefer mcpc over other SDKs?
+
+It depends on your use case. Roughly speaking, if
+
+1. you prefer your MCP servers or clients to perform tasks in the most
+ native way (e.g. manually manage memory allocation, equipped with
+ competitive performance, etc.).
+2. you wish your MCP servers or clients are distributed with minimum
+ software requirements (i.e. you don't want to force users to
+ have Python, Node.js or similar things installed on their machines)
+
+then mcpc is for you. Otherwise I would strongly recommend you consider
+Python, Javscript or other SDKs, they have more intuitive and
+easy-to-use high-level APIs for MCP protocol.
+
+
+<a id="license"></a>
+
+# License
+
+ The MIT License (MIT)
+
+ Copyright (c) 2025 Michael Lee <micl2e2 AT proton.me>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
+ this software and associated documentation files (the "Software"), to deal in
+ the Software without restriction, including without limitation the rights to
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ the Software, and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
diff --git a/third_party/mcpc/README.org b/third_party/mcpc/README.org
new file mode 100644
index 0000000..893f38d
--- /dev/null
+++ b/third_party/mcpc/README.org
@@ -0,0 +1,197 @@
+#+title: mcpc
+#+options: toc:1 num:nil
+
+* Overview
+:PROPERTIES:
+:CUSTOM_ID: overview
+:END:
+
+mcpc is a <b>M</b>odel <b>C</b>ontext <b>P</b>rotocol library in modern <b>C</b>. Its major goals are:
++ Provide the *most native* bridge between low-level infrastructure
+ and AI models
++ Explore the maximum capability of the *most recent* C programming language
+
+* MCP Features
+:PROPERTIES:
+:CUSTOM_ID: features
+:END:
+
+| Features | Server | Client |
+|-----------------+--------+--------|
+| Tool | ✅ | 🔨 |
+| Resource | ✅ | 🔨 |
+| Prompt | ✅ | 🔨 |
+| Completion | ✅ | 🔨 |
+| STDIO Transport | ✅ | 🔨 |
+| HTTP Transport | 🔨 | 🔨 |
+| Others | 🔨 | 🔨 |
+
+
+* Supported Platforms
+:PROPERTIES:
+:CUSTOM_ID: platforms
+:END:
+
+| | Linux | Windows | macOS |
+|-------+-------+---------+-------|
+| Linux | ✅ | ✅ | ✅ |
+
+Note: this table roughly shows the state of supporting on some platforms,
+for more detailed information, check the /Modern C Support/.
+
+
+
+* Modern C Support
+:PROPERTIES:
+:CUSTOM_ID: modernc
+:END:
+
+Since mcpc claims to implement everything with the most modern C
+possible, its building process becomes a little different than many
+existing building systems or process, the major
+difference is that: **we try to support as many C compilers as possible,
+even on one platform**.
+
+Ideally mcpc will be implemented fully in C23(currently the newest
+standard), but if a full C23 is missing on a platform, we
+either downgrade the C standard, or give up the support.
+
+This approach certainly has some benefits and consequences:
+
+1. The library can launch on as many platforms as possible, and be
+ able to compiled by as many C compilers as possible.
+2. There must be a minimum fallback version of C standard that the
+ library targets, currently it is C11.
+3. C23 features that are not patchable, or require non-trivial amount
+ of effort to patch, will not be used, e.g., [[https://open-std.org/JTC1/SC22/WG14/www/docs/n2508.pdf][N2508]], [[https://open-std.org/JTC1/SC22/WG14/www/docs/n2645.pdf][N2645]], etc.
+4. All safety checks and performance optimization will only focus on the
+ platforms with full C23 support.
+
+** Support Matrix
+
+=✅✅= : /implemented in C23/
+
+=🔨✅= : /implemented in C11~C17/
+
+=-= : /the platform does not exist, or work in progress/
+
+Linux:
+
+| | GLIBC 2.31 | GLIBC 2.36 | GLIBC 2.40 |
+|----------+------------------+------------------+------------------|
+| GCC 10 | 🔨✅ (Debian 11) | - | - |
+| GCC 12 | - | 🔨✅ (Debian 12) | - |
+| GCC 13 | - | - | - |
+| GCC 14 | - | - | ✅✅ (Fedora 41) |
+| Clang 14 | - | - | - |
+| Clang 16 | - | - | - |
+| Clang 18 | - | - | - |
+| Clang 19 | - | - | ✅✅ (Fedora 41) |
+
+
+Windows:
+
+| | CMD/PS | Cygwin | MinGW |
+|----------+--------+--------+-------|
+| MSVC 19 | 🔨✅ | - | - |
+| Clang 19 | - | - | - |
+| GCC 14 | - | ✅✅ | ✅✅ |
+
+
+macOS:
+
+| | macOS 13 | macOS 14 | macOS 15 |
+|----------+----------+----------+----------|
+| Clang 14 | 🔨✅ | - | - |
+| Clang 16 | - | 🔨✅ | 🔨✅ |
+| GCC 14 | 🔨✅ | - | - |
+
+
+* Building
+:PROPERTIES:
+:CUSTOM_ID: building
+:END:
+
+** Building (Linux, Cygwin, MSYS2, macOS, etc.)
+
+Prerequisites:
+- GNU Make
+
+Then
+#+begin_src
+cd mcpc
+make && make tst && make install
+#+end_src
+
+** Building (Windows CMD/PS/VS)
+
+Prerequisites:
+- Visual Studio
+- GNU Make (=winget install ezwinports.make=)
+
+Two options to build:
+
+*** 1. Build in CMD/PS
+
+Open "x64 Native Tools Command Prompt", then
+
+#+begin_src
+cd mcpc
+make && make tst
+#+end_src
+
+*** 2. Build in Visual Studio:
+
+"File" - "Open" - "CMake", "Build" - "Build All"
+
+#+begin_quote
+This approach is only for the better debugging experience on Windows,
+/"Build in CMD/PS"/ takes priority over this one.
+#+end_quote
+
+
+* Development
+:PROPERTIES:
+:CUSTOM_ID: development
+:END:
+
+API reference can be found at [[./misc/api.md][api.md]].
+
+Dependencies:
+- [[https://github.com/cesanta/mjson][mjson]] (already in-tree)
+
+** Code Contributing
+
+Despite we claim to use modern C, as standard as possible, and as
+modern as possible, we have some extra conventions, in order to render
+our code more readable, inclusive and scalable:
+
+1. Unless mentioned explicitly, we follow GNU C [[https://www.gnu.org/prep/standards/html_node/Writing-C.html][coding style]].
+2. Prefer functions over macros, use macros only if necessary.
+3. The type of variables should be reflected in the source code. Use of
+ keyword =auto= is thus forbidden.
+4. Names of defined variables that are exposed publicly should be in
+ uppercase, otherwise in lowercase.
+
+
+* FAQ
+
+** Should I prefer mcpc over other SDKs?
+
+It depends on your use case. Roughly speaking, if
+1. you prefer your MCP servers or clients to perform tasks in the most
+ native way (e.g. manually manage memory allocation, equipped with
+ competitive performance, etc.).
+2. you wish your MCP servers or clients are distributed with minimum
+ software requirements (i.e. you don't want to force users to
+ have Python, Node.js or similar things installed on their machines)
+then mcpc is for you. Otherwise I would strongly recommend you consider
+Python, Javscript or other SDKs, they have more intuitive and
+easy-to-use high-level APIs for MCP protocol.
+
+* License
+:PROPERTIES:
+:CUSTOM_ID: license
+:END:
+
+#+include: "LICENSE" src
diff --git a/third_party/mcpc/UPSTREAM.md b/third_party/mcpc/UPSTREAM.md
new file mode 100644
index 0000000..7951653
--- /dev/null
+++ b/third_party/mcpc/UPSTREAM.md
@@ -0,0 +1,12 @@
+# Vendored mcpc fork
+
+This directory is a project-maintained fork of
+<https://github.com/micl2e2/mcpc.git>.
+
+- Imported revision: `4025fddfedcfa7573803a47236b147733fcb3c07`
+- Upstream license: MIT (`LICENSE`)
+- Bundled mjson license: MIT (`licenses/LICENSE-mjson.txt`)
+
+The code is tracked directly by the parent repository rather than as a Git
+submodule. Upstream updates are reviewed and imported manually so the MCP
+2024-11-05 compatibility and stability patches in this fork remain auditable.
diff --git a/third_party/mcpc/licenses/LICENSE-mjson.txt b/third_party/mcpc/licenses/LICENSE-mjson.txt
new file mode 100644
index 0000000..cc47fea
--- /dev/null
+++ b/third_party/mcpc/licenses/LICENSE-mjson.txt
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Cesanta Software Limited
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/third_party/mcpc/mcpc/_c23_keywords.h b/third_party/mcpc/mcpc/_c23_keywords.h
new file mode 100644
index 0000000..87a64f7
--- /dev/null
+++ b/third_party/mcpc/mcpc/_c23_keywords.h
@@ -0,0 +1,30 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#if defined(MCPC_C23PTCH_KW1)
+
+#define nullptr ((void*)0)
+#define constexpr
+#define bool _Bool
+#define true (1)
+#define false (0)
+
+#endif
diff --git a/third_party/mcpc/mcpc/_c23_stdckdint.h b/third_party/mcpc/mcpc/_c23_stdckdint.h
new file mode 100644
index 0000000..4aea8ac
--- /dev/null
+++ b/third_party/mcpc/mcpc/_c23_stdckdint.h
@@ -0,0 +1,28 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#if defined(MOCK_PTCH_C23CKD1)
+
+#define ckd_add(R, A, B) __builtin_add_overflow((A), (B), (R))
+#define ckd_sub(R, A, B) __builtin_sub_overflow((A), (B), (R))
+#define ckd_mul(R, A, B) __builtin_mul_overflow((A), (B), (R))
+
+#endif
diff --git a/third_party/mcpc/mcpc/_c23_uchar.h b/third_party/mcpc/mcpc/_c23_uchar.h
new file mode 100644
index 0000000..41fc382
--- /dev/null
+++ b/third_party/mcpc/mcpc/_c23_uchar.h
@@ -0,0 +1,31 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#if defined(MCPC_C23PTCH_UCHAR1)
+typedef char char8_t;
+#elif defined(MCPC_C23PTCH_UCHAR2)
+typedef unsigned char char8_t;
+#else
+#include <uchar.h>
+#endif
+
+typedef char8_t ch8_t;
+
diff --git a/third_party/mcpc/mcpc/anydata.h b/third_party/mcpc/mcpc/anydata.h
new file mode 100644
index 0000000..a3a0712
--- /dev/null
+++ b/third_party/mcpc/mcpc/anydata.h
@@ -0,0 +1,101 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h_pub_anydata
+#define h_pub_anydata
+
+#include <stdint.h>
+
+#include "_c23_keywords.h"
+
+enum mcpc_anytype
+{
+ MCPC_NONE = 0,
+ MCPC_I8,
+ MCPC_U8,
+ MCPC_I16,
+ MCPC_U16,
+ MCPC_I32,
+ MCPC_U32,
+ MCPC_I64,
+ MCPC_U64,
+ MCPC_U8STR,
+ MCPC_I8S,
+ MCPC_U8S,
+ MCPC_I16S,
+ MCPC_U16S,
+ MCPC_I32S,
+ MCPC_U32S,
+ MCPC_I64S,
+ MCPC_U64S,
+ MCPC_FUNC,
+};
+typedef enum mcpc_anytype mcpc_anytype_t;
+
+typedef struct mcpc_anydata mcpc_anydata_t;
+
+typedef struct mcpc_anypool mcpc_anypool_t;
+
+
+#ifdef MCPC_C23GIVUP_FIXENUM
+enum mcpc_mime
+#else
+enum mcpc_mime:uint16_t
+#endif
+{
+ MCPC_MIME_NONE = 0,
+ MCPC_MIME_TEXT_PLAIN,
+ MCPC_MIME_IMAGE_PNG,
+ MCPC_MIME_IMAGE_JPEG,
+ MCPC_MIME_IMAGE_GIF,
+ MCPC_MIME_AUDIO_MPEG,
+ MCPC_MIME_AUDIO_WAV,
+ MCPC_MIME_VIDEO_MPEG,
+ MCPC_MIME_VIDEO_AVI,
+ MCPC_MIME_APPLI_OCTS,
+ MCPC_MIME_APPLI_JSON,
+ MCPC_MIME_APPLI_PDF,
+};
+typedef enum mcpc_mime mcpc_mime_t;
+
+
+static inline bool
+mcpc_anytype_is_intlike (mcpc_anytype_t typ)
+{
+ if (typ == MCPC_I8 || typ == MCPC_I16 || typ == MCPC_I32 || typ == MCPC_I64 || typ == MCPC_U8 || typ == MCPC_U16
+ || typ == MCPC_U32 || typ == MCPC_U64 || typ == MCPC_FUNC)
+ return true;
+
+ return false;
+}
+
+static inline bool
+mcpc_anytype_is_arrlike (mcpc_anytype_t typ)
+{
+ if (typ == MCPC_U8STR || typ == MCPC_I8S || typ == MCPC_I16S || typ == MCPC_I32S || typ == MCPC_I64S
+ || typ == MCPC_U8S || typ == MCPC_U16S || typ == MCPC_U32S || typ == MCPC_U64S)
+ return true;
+
+ return false;
+}
+
+
+#endif
diff --git a/third_party/mcpc/mcpc/errcode.h b/third_party/mcpc/mcpc/errcode.h
new file mode 100644
index 0000000..8b15981
--- /dev/null
+++ b/third_party/mcpc/mcpc/errcode.h
@@ -0,0 +1,50 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h_pub_errcode
+#define h_pub_errcode
+
+enum mcpc_errcode
+{
+ MCPC_EC_0 = 0,
+ MCPC_EC_BUG,
+ MCPC_EC_BUFCAP,
+ MCPC_EC_ANYDATA_LONGMETA1,
+ MCPC_EC_ANYDATA_LONGMETA2,
+ MCPC_EC_LONGTOOLNAME,
+ MCPC_EC_LONGTOOLDESC,
+ MCPC_EC_LONGTPROPNAME,
+ MCPC_EC_LONGTPROPDESC,
+ MCPC_EC_TOOLPROP_NOTFOUND,
+ MCPC_EC_TOOLCALL_USERERR,
+ MCPC_EC_CONNPOOL_NOTADD,
+ MCPC_EC_SVIMPL_SOCKET,
+ MCPC_EC_SVIMPL_BIND,
+ MCPC_EC_SVIMPL_LISTEN,
+ MCPC_EC_SVIMPL_TCP_INSUF_BUF,
+ MCPC_EC_ANYDATA_NOTFOUND,
+ MCPC_EC_ANYDATA_LONGNAME,
+ MCPC_EC_PRMPTARG_NOTFOUND,
+ MCPC_EC_JSON_FIELD_INVALID,
+};
+typedef enum mcpc_errcode mcpc_errcode_t;
+
+#endif
diff --git a/third_party/mcpc/mcpc/mcpc.h b/third_party/mcpc/mcpc/mcpc.h
new file mode 100644
index 0000000..163fba0
--- /dev/null
+++ b/third_party/mcpc/mcpc/mcpc.h
@@ -0,0 +1,33 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h_pub_mcpc
+#define h_pub_mcpc
+
+#include "visb.h"
+#include "errcode.h"
+#include "tool.h"
+#include "rsc.h"
+#include "server.h"
+#include "ucbr.h"
+
+
+#endif
diff --git a/third_party/mcpc/mcpc/prmpt.h b/third_party/mcpc/mcpc/prmpt.h
new file mode 100644
index 0000000..e035c2f
--- /dev/null
+++ b/third_party/mcpc/mcpc/prmpt.h
@@ -0,0 +1,98 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h_pub_prmpt
+#define h_pub_prmpt
+
+#include <stddef.h>
+
+#include "_c23_uchar.h"
+#include "visb.h"
+
+#include "anydata.h"
+#include "errcode.h"
+#include "ucbr.h"
+
+typedef mcpc_anydata_t mcpc_prmptarghint_t;
+
+typedef mcpc_anydata_t mcpc_prmptarg_t;
+
+typedef mcpc_anydata_t mcpc_prmpt_t;
+
+typedef void (*mcpc_prmpt_callcb_t) (const mcpc_prmpt_t *prmpt, mcpc_ucbr_t *ucbr);
+
+MCPC_API mcpc_prmptarg_t *
+mcpc_prmptarg_new (const char8_t *name, const size_t name_len, const char8_t *desc, const size_t desc_len);
+
+MCPC_API mcpc_prmptarg_t *
+mcpc_prmptarg_new2 (const char8_t *nament, const char8_t *descnt);
+
+MCPC_API mcpc_prmptarg_t *
+mcpc_prmptarg_new3 (const char8_t *nament);
+
+MCPC_API mcpc_errcode_t
+mcpc_prmptarg_get_u8str (const mcpc_prmptarg_t *prmptarg, char8_t *ret, size_t ret_cap, size_t *ret_len);
+
+MCPC_API mcpc_errcode_t
+mcpc_prmptarg_getref_u8str (const mcpc_prmptarg_t *prmptarg, const char8_t **ret, size_t *ret_len);
+
+MCPC_API mcpc_errcode_t
+mcpc_prmptarg_add_hint_printf8 (mcpc_prmptarg_t *prmptarg, const char8_t *fmt, ...);
+
+MCPC_API mcpc_prmpt_t *
+mcpc_prmpt_new (const char8_t *name, const size_t name_len, const char8_t *desc, const size_t desc_len);
+
+MCPC_API mcpc_prmpt_t *
+mcpc_prmpt_new2 (const char8_t *nament, const char8_t *descnt);
+
+MCPC_API mcpc_prmpt_t *
+mcpc_prmpt_new3 (const char8_t *nament);
+
+MCPC_API mcpc_errcode_t
+mcpc_prmpt_addfre_prmptarg (mcpc_prmpt_t *prmpt, mcpc_prmptarg_t *prmptarg);
+
+MCPC_API void
+mcpc_prmpt_addcpy_prmptarg (mcpc_prmpt_t *prmpt, mcpc_prmptarg_t *prmptarg);
+
+#define mcpc_prmpt_add_prmptarg mcpc_prmpt_addfre_prmptarg
+
+MCPC_API const mcpc_prmptarg_t *
+mcpc_prmpt_get_prmptarg (const mcpc_prmpt_t *prmpt, size_t idx);
+
+MCPC_API size_t
+mcpc_prmpt_get_prmptarg_len (const mcpc_prmpt_t *prmpt);
+
+MCPC_API const mcpc_prmptarg_t *
+mcpc_prmpt_get_next_prmptarg (const mcpc_prmpt_t *prmpt);
+
+MCPC_API const mcpc_prmptarg_t *
+mcpc_prmpt_getbyname_prmptarg (const mcpc_prmpt_t *prmpt, const char8_t *parg_name, size_t parg_name_len);
+
+MCPC_API const mcpc_prmptarg_t *
+mcpc_prmpt_getbynament_prmptarg (const mcpc_prmpt_t *prmpt, const char8_t *parg_nament);
+
+MCPC_API mcpc_errcode_t
+mcpc_prmpt_get_callcb (const mcpc_prmpt_t *prmpt, mcpc_prmpt_callcb_t *ret);
+
+MCPC_API mcpc_errcode_t
+mcpc_prmpt_set_callcb (mcpc_prmpt_t *prmpt, mcpc_prmpt_callcb_t cb);
+
+#endif
diff --git a/third_party/mcpc/mcpc/rsc.h b/third_party/mcpc/mcpc/rsc.h
new file mode 100644
index 0000000..7c98bb2
--- /dev/null
+++ b/third_party/mcpc/mcpc/rsc.h
@@ -0,0 +1,60 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h_pub_rsc
+#define h_pub_rsc
+
+#include "_c23_uchar.h"
+#include "visb.h"
+
+#include "anydata.h"
+#include "errcode.h"
+
+typedef mcpc_anydata_t mcpc_rsc_t;
+
+MCPC_API mcpc_rsc_t *
+mcpc_rsc_new (const char8_t *uri, const size_t uri_len, const char8_t *name, const size_t name_len);
+
+MCPC_API mcpc_rsc_t *
+mcpc_rsc_new2 (const char8_t *uri_nt, const char8_t *nament);
+
+MCPC_API mcpc_rsc_t *
+mcpc_rsc_new3 (mcpc_mime_t mime, const char8_t *uri_nt, const char8_t *nament);
+
+MCPC_API void
+mcpc_rsc_free (mcpc_rsc_t *rsc);
+
+MCPC_API mcpc_errcode_t
+mcpc_rsc_get_uri (const mcpc_rsc_t *rsc, char8_t *ret, size_t ret_cap, size_t *ret_len);
+
+MCPC_API mcpc_errcode_t
+mcpc_rsc_getref_uri (const mcpc_rsc_t *rsc, const char8_t **ret, size_t *ret_len);
+
+MCPC_API mcpc_errcode_t
+mcpc_rsc_getref_name (const mcpc_rsc_t *rsc, const char8_t **ret, size_t *ret_len);
+
+MCPC_API mcpc_errcode_t
+mcpc_rsc_get_mime (const mcpc_rsc_t *rsc, mcpc_mime_t *ret);
+
+MCPC_API void
+mcpc_rsc_set_mime (mcpc_rsc_t *rsc, mcpc_mime_t mime);
+
+#endif
diff --git a/third_party/mcpc/mcpc/server.h b/third_party/mcpc/mcpc/server.h
new file mode 100644
index 0000000..2ed28eb
--- /dev/null
+++ b/third_party/mcpc/mcpc/server.h
@@ -0,0 +1,89 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h_pub_server
+#define h_pub_server
+
+#include <stdio.h>
+
+#include "visb.h"
+#include "_c23_keywords.h"
+
+#include "errcode.h"
+#include "tool.h"
+#include "prmpt.h"
+#include "rsc.h"
+
+
+typedef struct mcpc_server mcpc_server_t;
+
+MCPC_API mcpc_server_t *
+mcpc_server_new_iostrm (const FILE *const strm_in, const FILE *const strm_out);
+
+MCPC_API mcpc_server_t *
+mcpc_server_new_tcp ();
+
+MCPC_API mcpc_errcode_t
+mcpc_server_set_name (mcpc_server_t *sv, const char8_t *name, size_t name_len);
+
+MCPC_API mcpc_errcode_t
+mcpc_server_set_nament (mcpc_server_t *sv, const char8_t *nament);
+
+MCPC_API mcpc_errcode_t
+mcpc_server_capa_enable_prmt (mcpc_server_t *sv);
+
+MCPC_API mcpc_errcode_t
+mcpc_server_capa_enable_prmt_listchg (mcpc_server_t *sv);
+
+MCPC_API mcpc_errcode_t
+mcpc_server_capa_enable_tool (mcpc_server_t *sv);
+
+MCPC_API mcpc_errcode_t
+mcpc_server_capa_enable_tool_listchg (mcpc_server_t *sv);
+
+MCPC_API mcpc_errcode_t
+mcpc_server_capa_enable_rsc (mcpc_server_t *sv);
+
+MCPC_API mcpc_errcode_t
+mcpc_server_capa_enable_rsc_subscr (mcpc_server_t *sv);
+
+MCPC_API mcpc_errcode_t
+mcpc_server_capa_enable_rsc_listchg (mcpc_server_t *sv);
+
+MCPC_API mcpc_errcode_t
+mcpc_server_capa_enable_complt (mcpc_server_t *sv);
+
+MCPC_API mcpc_errcode_t
+mcpc_server_add_tool (mcpc_server_t *sv, mcpc_tool_t *tool);
+
+MCPC_API mcpc_errcode_t
+mcpc_server_add_prmpt (mcpc_server_t *sv, mcpc_prmpt_t *prmpt);
+
+MCPC_API mcpc_errcode_t
+mcpc_server_add_rsc (mcpc_server_t *sv, mcpc_rsc_t *rsc);
+
+MCPC_API mcpc_errcode_t
+mcpc_server_start (mcpc_server_t *sv);
+
+MCPC_API mcpc_errcode_t
+mcpc_server_close (mcpc_server_t *sv);
+
+#endif
diff --git a/third_party/mcpc/mcpc/tool.h b/third_party/mcpc/mcpc/tool.h
new file mode 100644
index 0000000..931e914
--- /dev/null
+++ b/third_party/mcpc/mcpc/tool.h
@@ -0,0 +1,85 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h_pub_tool
+#define h_pub_tool
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "_c23_uchar.h"
+#include "visb.h"
+
+#include "anydata.h"
+#include "errcode.h"
+#include "ucbr.h"
+
+typedef mcpc_anydata_t mcpc_toolprop_t;
+
+typedef mcpc_anydata_t mcpc_tool_t;
+
+typedef void (*mcpc_tcallcb_t) (const mcpc_tool_t *tool, mcpc_ucbr_t *ucbr);
+
+MCPC_API mcpc_toolprop_t *
+mcpc_toolprop_new (const char8_t *name, const size_t name_len, const char8_t *desc, const size_t desc_len, mcpc_anytype_t typ);
+
+MCPC_API mcpc_toolprop_t *
+mcpc_toolprop_new2 (const char8_t *nament, const char8_t *descnt, mcpc_anytype_t typ);
+
+MCPC_API bool
+mcpc_toolprop_is_required (const mcpc_toolprop_t *toolprop);
+
+MCPC_API mcpc_errcode_t
+mcpc_toolprop_set_required (mcpc_toolprop_t *toolprop, bool required);
+
+MCPC_API mcpc_tool_t *
+mcpc_tool_new (const char8_t *name, const size_t name_len, const char8_t *desc, const size_t desc_len);
+
+MCPC_API mcpc_tool_t *
+mcpc_tool_new2 (const char8_t *nament, const char8_t *descnt);
+
+MCPC_API void
+mcpc_tool_addfre_toolprop (mcpc_tool_t *tool, mcpc_toolprop_t *toolprop);
+
+MCPC_API void
+mcpc_tool_addcpy_toolprop (mcpc_tool_t *tool, mcpc_toolprop_t *toolprop);
+
+#define mcpc_tool_add_toolprop mcpc_tool_addfre_toolprop
+
+MCPC_API const mcpc_toolprop_t *
+mcpc_tool_get_toolprop (const mcpc_tool_t *tool, size_t idx);
+
+MCPC_API size_t
+mcpc_tool_get_toolprop_len (const mcpc_tool_t *tool);
+
+MCPC_API mcpc_errcode_t
+mcpc_tool_get_tpropval_i32 (const mcpc_tool_t *tool, const char8_t *tprop_nament, int32_t *ret);
+
+MCPC_API mcpc_errcode_t
+mcpc_tool_get_tpropval_u8str (const mcpc_tool_t *tool, const char8_t *tprop_nament, char8_t *ret, size_t ret_cap, size_t *ret_len);
+
+MCPC_API mcpc_errcode_t
+mcpc_tool_get_call_cb (const mcpc_tool_t *tool, mcpc_tcallcb_t *ret);
+
+MCPC_API mcpc_errcode_t
+mcpc_tool_set_call_cb (mcpc_tool_t *tool, mcpc_tcallcb_t cb);
+
+#endif
diff --git a/third_party/mcpc/mcpc/ucbr.h b/third_party/mcpc/mcpc/ucbr.h
new file mode 100644
index 0000000..03620ad
--- /dev/null
+++ b/third_party/mcpc/mcpc/ucbr.h
@@ -0,0 +1,55 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h_pub_ucbr
+#define h_pub_ucbr
+
+#include <stdarg.h>
+
+#include "_c23_uchar.h"
+#include "visb.h"
+
+#include "anydata.h"
+#include "errcode.h"
+
+typedef mcpc_anypool_t mcpc_ucbr_t;
+
+MCPC_API void
+mcpc_ucbr_toolcall_add_text_printf8 (mcpc_ucbr_t *ucbr, const char8_t *fmt, ...);
+
+MCPC_API void
+mcpc_ucbr_toolcall_add_errmsg_printf8 (mcpc_ucbr_t *ucbr, const char8_t *fmt, ...);
+
+MCPC_API void
+mcpc_ucbr_prmptget_add_user_printf8 (mcpc_ucbr_t *ucbr, const char8_t *fmt, ...);
+
+MCPC_API void
+mcpc_ucbr_prmptget_add_assist_printf8 (mcpc_ucbr_t *ucbr, const char8_t *fmt, ...);
+
+#define mcpc_toolcall_result_add_text_printf8 mcpc_ucbr_toolcall_add_text_printf8
+
+#define mcpc_toolcall_result_add_errmsg_printf8 mcpc_ucbr_toolcall_add_errmsg_printf8
+
+#define mcpc_prmptget_result_add_user_printf8 mcpc_ucbr_prmptget_add_user_printf8
+
+#define mcpc_prmptget_result_add_assist_printf8 mcpc_ucbr_prmptget_add_assist_printf8
+
+#endif
diff --git a/third_party/mcpc/mcpc/visb.h b/third_party/mcpc/mcpc/visb.h
new file mode 100644
index 0000000..6a96a1f
--- /dev/null
+++ b/third_party/mcpc/mcpc/visb.h
@@ -0,0 +1,38 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h_pub_visb
+#define h_pub_visb
+
+/* *INDENT-OFF* */
+#if defined(_WIN32) && defined(MCPC_SHARED)
+/**/ #ifdef MCPC_EXPORTS
+/**//**/ #define MCPC_API __declspec(dllexport)
+/**/ #else
+/**//**/ #define MCPC_API __declspec(dllimport)
+/**/ #endif
+#else
+/**/ #define MCPC_API
+#endif
+/* *INDENT-ON* */
+
+
+#endif
diff --git a/third_party/mcpc/misc/api.md b/third_party/mcpc/misc/api.md
new file mode 100644
index 0000000..f5a9618
--- /dev/null
+++ b/third_party/mcpc/misc/api.md
@@ -0,0 +1,1112 @@
+
+# Table of Contents
+
+- [Server](#server)
+ - [`mcpc_server_t`](#=mcpc_server_t=)
+ - [`mcpc_server_new_iostrm`](#=mcpc_server_new_iostrm=)
+ - [`mcpc_server_new_tcp`](#=mcpc_server_new_tcp=)
+ - [`mcpc_server_set_nament`](#=mcpc_server_set_nament=)
+ - [`mcpc_server_capa_enable_prmt`](#=mcpc_server_capa_enable_prmt=)
+ - [`mcpc_server_capa_enable_prmt_listchg`](#=mcpc_server_capa_enable_prmt_listchg=)
+ - [`mcpc_server_capa_enable_tool`](#=mcpc_server_capa_enable_tool=)
+ - [`mcpc_server_capa_enable_tool_listchg`](#=mcpc_server_capa_enable_tool_listchg=)
+ - [`mcpc_server_capa_enable_rsc`](#=mcpc_server_capa_enable_rsc=)
+ - [`mcpc_server_capa_enable_rsc_subscr`](#=mcpc_server_capa_enable_rsc_subscr=)
+ - [`mcpc_server_capa_enable_rsc_listchg`](#=mcpc_server_capa_enable_rsc_listchg=)
+ - [`mcpc_server_capa_enable_complt`](#=mcpc_server_capa_enable_complt=)
+ - [`mcpc_server_add_tool`](#=mcpc_server_add_tool=)
+ - [`mcpc_server_add_prmpt`](#=mcpc_server_add_prmpt=)
+ - [`mcpc_server_add_rsc`](#=mcpc_server_add_rsc=)
+ - [`mcpc_server_start`](#=mcpc_server_start=)
+ - [`mcpc_server_close`](#=mcpc_server_close=)
+- [Tool](#org386a1c2)
+ - [`mcpc_tool_t`](#=mcpc_tool_t=)
+ - [`mcpc_toolprop_t`](#=mcpc_toolprop_t=)
+ - [`mcpc_tcallcb_t`](#=mcpc_tcallcb_t=)
+ - [`mcpc_toolprop_new`](#=mcpc_toolprop_new=)
+ - [`mcpc_toolprop_new2`](#=mcpc_toolprop_new2=)
+ - [`mcpc_toolprop_is_required`](#=mcpc_toolprop_is_required=)
+ - [`mcpc_toolprop_set_required`](#=mcpc_toolprop_set_required=)
+ - [`mcpc_tool_new`](#=mcpc_tool_new=)
+ - [`mcpc_tool_new2`](#=mcpc_tool_new2=)
+ - [`mcpc_tool_addfre_toolprop`](#=mcpc_tool_addfre_toolprop=)
+ - [`mcpc_tool_addcpy_toolprop`](#=mcpc_tool_addcpy_toolprop=)
+ - [`mcpc_tool_add_toolprop`](#=mcpc_tool_add_toolprop=)
+ - [`mcpc_tool_get_toolprop` (deprecate)](#=mcpc_tool_get_toolprop=)
+ - [`mcpc_tool_get_toolprop_len`](#=mcpc_tool_get_toolprop_len=)
+ - [`mcpc_tool_get_tpropval_i32`](#=mcpc_tool_get_tpropval_i32=)
+ - [`mcpc_tool_get_tpropval_u8str`](#=mcpc_tool_get_tpropval_u8str=)
+ - [`mcpc_tool_get_call_cb`](#=mcpc_tool_get_call_cb=)
+ - [`mcpc_tool_set_call_cb`](#=mcpc_tool_set_call_cb=)
+ - [`mcpc_toolcall_result_add_text_printf8`](#=mcpc_toolcall_result_add_text_printf8=)
+ - [`mcpc_toolcall_result_add_errmsg_printf8`](#=mcpc_toolcall_result_add_errmsg_printf8=)
+- [Resource](#org52ee5a7)
+ - [`mcpc_rsc_t`](#=mcpc_rsc_t=)
+ - [`mcpc_rsc_new`](#=mcpc_rsc_new=)
+ - [`mcpc_rsc_new2`](#=mcpc_rsc_new2=)
+ - [`mcpc_rsc_new3`](#=mcpc_rsc_new3=)
+ - [`mcpc_rsc_free`](#=mcpc_rsc_free=)
+ - [`mcpc_rsc_get_uri`](#=mcpc_rsc_get_uri=)
+ - [`mcpc_rsc_getref_uri`](#=mcpc_rsc_getref_uri=)
+ - [`mcpc_rsc_getref_name`](#=mcpc_rsc_getref_name=)
+ - [`mcpc_rsc_get_mime`](#=mcpc_rsc_get_mime=)
+- [Prompt](#org211140d)
+ - [`mcpc_prmpt_t`](#=mcpc_prmpt_t=)
+ - [`mcpc_prmptarg_t`](#=mcpc_prmptarg_t=)
+ - [`mcpc_prmptarghint_t`](#=mcpc_prmptarghint_t=)
+ - [`mcpc_prmpt_callcb_t`](#=mcpc_prmpt_callcb_t=)
+ - [`mcpc_prmptarg_new`](#=mcpc_prmptarg_new=)
+ - [`mcpc_prmptarg_new2`](#=mcpc_prmptarg_new2=)
+ - [`mcpc_prmptarg_new3`](#=mcpc_prmptarg_new3=)
+ - [`mcpc_prmpt_new`](#=mcpc_prmpt_new=)
+ - [`mcpc_prmpt_new2`](#=mcpc_prmpt_new2=)
+ - [`mcpc_prmpt_new3`](#=mcpc_prmpt_new3=)
+ - [`mcpc_prmpt_addfre_prmptarg`](#=mcpc_prmpt_addfre_prmptarg=)
+ - [`mcpc_prmpt_addcpy_prmptarg`](#=mcpc_prmpt_addcpy_prmptarg=)
+ - [`mcpc_prmpt_add_prmptarg`](#=mcpc_prmpt_add_prmptarg=)
+ - [`mcpc_prmpt_get_prmptarg` (deprecated)](#=mcpc_prmpt_get_prmptarg=)
+ - [`mcpc_prmpt_get_prmptarg_len`](#=mcpc_prmpt_get_prmptarg_len=)
+ - [`mcpc_prmpt_get_next_prmptarg`](#=mcpc_prmpt_get_next_prmptarg=)
+ - [`mcpc_prmpt_getbyname_prmptarg`](#=mcpc_prmpt_getbyname_prmptarg=)
+ - [`mcpc_prmpt_getbynament_prmptarg`](#=mcpc_prmpt_getbynament_prmptarg=)
+ - [`mcpc_prmpt_get_callcb`](#=mcpc_prmpt_get_callcb=)
+ - [`mcpc_prmpt_set_callcb`](#=mcpc_prmpt_set_callcb=)
+ - [`mcpc_prmptarg_get_u8str`](#=mcpc_prmptarg_get_u8str=)
+ - [`mcpc_prmptarg_getref_u8str`](#=mcpc_prmptarg_getref_u8str=)
+ - [`mcpc_prmptarg_add_hint_printf8`](#=mcpc_prmptarg_add_hint_printf8=)
+ - [`mcpc_prmptget_result_add_user_printf8`](#=mcpc_prmptget_result_add_user_printf8=)
+ - [`mcpc_prmptget_result_add_assist_printf8`](#=mcpc_prmptget_result_add_assist_printf8=)
+- [Others](#org6f84d9e)
+ - [`mcpc_errcode_t`](#=mcpc_)
+ - [`mcpc_anytype_t`](#=mcpc_anytype_t=)
+ - [`mcpc_mime_t`](#=mcpc_mime_t=)
+ - [`mcpc_ucbr_t`](#=mcpc_ucbr_t=)
+ - [`mcpc_ucbr_toolcall_add_text_printf8`](#=mcpc_ucbr_toolcall_add_text_printf8=)
+ - [`mcpc_ucbr_toolcall_add_errmsg_printf8`](#=mcpc_ucbr_toolcall_add_errmsg_printf8=)
+ - [`mcpc_ucbr_prmptget_add_user_printf8`](#=mcpc_ucbr_prmptget_add_user_printf8=)
+ - [`mcpc_ucbr_prmptget_add_assist_printf8`](#=mcpc_ucbr_prmptget_add_assist_printf8=)
+
+
+
+<a id="server"></a>
+
+# Server
+
+
+<a id="=mcpc_server_t="></a>
+
+## `mcpc_server_t`
+
+Server instance
+
+
+<a id="=mcpc_server_new_iostrm="></a>
+
+## `mcpc_server_new_iostrm`
+
+Create a I/O streams-base server.
+
+ mcpc_server_t *
+ mcpc_server_new_iostrm (const FILE *const strm_in, const FILE *const strm_out);
+
+`strm_in`: input stream
+
+`strm_out`: output stream
+
+
+<a id="=mcpc_server_new_tcp="></a>
+
+## `mcpc_server_new_tcp`
+
+Create a TCP-based server.
+
+ mcpc_server_t*
+ mcpc_server_new_tcp ();
+
+
+<a id="=mcpc_server_set_nament="></a>
+
+## `mcpc_server_set_nament`
+
+Set server's name.
+
+ mcpc_errcode_t
+ mcpc_server_set_nament (mcpc_server_t *sv, const char8_t *nament);
+
+`sv`: server
+
+`nament`: null-terminated name. Must be a UTF-8 string
+
+
+<a id="=mcpc_server_capa_enable_prmt="></a>
+
+## `mcpc_server_capa_enable_prmt`
+
+Enable server's general capability of *Prompt*.
+
+ mcpc_errcode_t
+ mcpc_server_capa_enable_prmt (mcpc_server_t *sv);
+
+`sv`: server
+
+
+<a id="=mcpc_server_capa_enable_prmt_listchg="></a>
+
+## `mcpc_server_capa_enable_prmt_listchg`
+
+Enable server's capability of notification for Prompt list updates.
+
+ mcpc_errcode_t
+ mcpc_server_capa_enable_prmt_listchg (mcpc_server_t *sv);
+
+`sv`: server
+
+
+<a id="=mcpc_server_capa_enable_tool="></a>
+
+## `mcpc_server_capa_enable_tool`
+
+Enable server's general capability of *Prompt*.
+
+ mcpc_errcode_t
+ mcpc_server_capa_enable_tool (mcpc_server_t *sv);
+
+`sv`: server
+
+
+<a id="=mcpc_server_capa_enable_tool_listchg="></a>
+
+## `mcpc_server_capa_enable_tool_listchg`
+
+Enable server's capability of notification for Prompt list updates.
+
+ mcpc_errcode_t
+ mcpc_server_capa_enable_tool_listchg (mcpc_server_t *sv);
+
+`sv`: server
+
+
+<a id="=mcpc_server_capa_enable_rsc="></a>
+
+## `mcpc_server_capa_enable_rsc`
+
+Enable server's general capability of *Resource*.
+
+ mcpc_errcode_t
+ mcpc_server_capa_enable_rsc (mcpc_server_t *sv);
+
+`sv`: server
+
+
+<a id="=mcpc_server_capa_enable_rsc_subscr="></a>
+
+## `mcpc_server_capa_enable_rsc_subscr`
+
+Enable server's capability of notification for Resource subscription.
+
+ mcpc_errcode_t
+ mcpc_server_capa_enable_rsc_subscr (mcpc_server_t *sv);
+
+`sv`: server
+
+
+<a id="=mcpc_server_capa_enable_rsc_listchg="></a>
+
+## `mcpc_server_capa_enable_rsc_listchg`
+
+Enable server's capability of notification for Prompt list updates.
+
+ mcpc_errcode_t
+ mcpc_server_capa_enable_rsc_listchg (mcpc_server_t *sv);
+
+`sv`: server
+
+
+<a id="=mcpc_server_capa_enable_complt="></a>
+
+## `mcpc_server_capa_enable_complt`
+
+Enable server's general capability of *Completion*.
+
+ mcpc_errcode_t
+ mcpc_server_capa_enable_complt (mcpc_server_t *sv);
+
+`sv`: server
+
+
+<a id="=mcpc_server_add_tool="></a>
+
+## `mcpc_server_add_tool`
+
+Add a *Tool* to a server.
+
+ mcpc_errcode_t
+ mcpc_server_add_tool (mcpc_server_t *sv, mcpc_tool_t *tool);
+
+`sv`: server
+
+`tool`: tool, it will be automatically freed
+
+
+<a id="=mcpc_server_add_prmpt="></a>
+
+## `mcpc_server_add_prmpt`
+
+Add a *Prompt* to a server.
+
+ mcpc_errcode_t
+ mcpc_server_add_prmpt (mcpc_server_t *sv, mcpc_prmpt_t *prmpt);
+
+`sv`: server
+
+`prmpt`: prompt, it will be automatically freed
+
+
+<a id="=mcpc_server_add_rsc="></a>
+
+## `mcpc_server_add_rsc`
+
+Add a *Resource* to a server.
+
+ mcpc_errcode_t
+ mcpc_server_add_rsc (mcpc_server_t *sv, mcpc_rsc_t *rsc);
+
+`sv`: server
+
+`prmpt`: prompt, it will be automatically freed
+
+
+<a id="=mcpc_server_start="></a>
+
+## `mcpc_server_start`
+
+Start a server.
+
+ mcpc_errcode_t
+ mcpc_server_start (mcpc_server_t *sv);
+
+`sv`: server
+
+
+<a id="=mcpc_server_close="></a>
+
+## `mcpc_server_close`
+
+Close a server.
+
+ mcpc_errcode_t
+ mcpc_server_close (mcpc_server_t *sv);
+
+`sv`: server
+
+
+<a id="org386a1c2"></a>
+
+# Tool
+
+
+<a id="=mcpc_tool_t="></a>
+
+## `mcpc_tool_t`
+
+Tool
+
+
+<a id="=mcpc_toolprop_t="></a>
+
+## `mcpc_toolprop_t`
+
+Tool property
+
+
+<a id="=mcpc_tcallcb_t="></a>
+
+## `mcpc_tcallcb_t`
+
+The callback function of while doing tool calling.
+
+
+<a id="=mcpc_toolprop_new="></a>
+
+## `mcpc_toolprop_new`
+
+ mcpc_toolprop_t *
+ mcpc_toolprop_new (const char8_t *name, const size_t name_len, const char8_t *desc, const size_t desc_len, mcpc_anytype_t typ);
+
+`name`: *tool* property's name
+
+`name_len`: the length of *tool* property's name
+
+`desc`: *tool* property's description
+
+`desc_len`: the length of *tool* property's description
+
+
+<a id="=mcpc_toolprop_new2="></a>
+
+## `mcpc_toolprop_new2`
+
+ mcpc_toolprop_t *
+ mcpc_toolprop_new2 (const char8_t *nament, const char8_t *descnt, mcpc_anytype_t typ);
+
+`nament`: *tool* property's name, must be null-terminated
+
+`descnt`: *tool* property's description, must be null-terminated
+
+## `mcpc_toolprop_is_required`
+
+ bool
+ mcpc_toolprop_is_required (const mcpc_toolprop_t *toolprop);
+
+Returns whether the property is listed in the tool input schema's `required` array.
+
+## `mcpc_toolprop_set_required`
+
+ mcpc_errcode_t
+ mcpc_toolprop_set_required (mcpc_toolprop_t *toolprop, bool required);
+
+Sets the property requirement used by both schema serialization and request handling.
+
+
+<a id="=mcpc_tool_new="></a>
+
+## `mcpc_tool_new`
+
+ mcpc_tool_t *
+ mcpc_tool_new (const char8_t *name, const size_t name_len, const char8_t *desc, const size_t desc_len);
+
+`name`: *tool*'s name
+
+`name_len`: the length of *tool*'s name
+
+`desc`: *tool*'s description
+
+`desc_len`: the length of *tool*'s description
+
+
+<a id="=mcpc_tool_new2="></a>
+
+## `mcpc_tool_new2`
+
+ mcpc_tool_t *
+ mcpc_tool_new2 (const char8_t *nament, const char8_t *descnt);
+
+`nament`: *tool*'s name, must be null-terminated
+
+`descnt`: *tool*'s description, must be null-terminated
+
+
+<a id="=mcpc_tool_addfre_toolprop="></a>
+
+## `mcpc_tool_addfre_toolprop`
+
+ void
+ mcpc_tool_addfre_toolprop (mcpc_tool_t *tool, mcpc_toolprop_t *toolprop);
+
+`tool`: *tool*
+
+`toolprop`: *tool* property
+
+
+<a id="=mcpc_tool_addcpy_toolprop="></a>
+
+## `mcpc_tool_addcpy_toolprop`
+
+ void
+ mcpc_tool_addcpy_toolprop (mcpc_tool_t *tool, mcpc_toolprop_t *toolprop);
+
+`tool`: *tool*
+
+`toolprop`: *tool* property
+
+
+<a id="=mcpc_tool_add_toolprop="></a>
+
+## `mcpc_tool_add_toolprop`
+
+Alias of `mcpc_tool_addfre_toolprop`
+
+
+<a id="=mcpc_tool_get_toolprop="></a>
+
+## `mcpc_tool_get_toolprop` (deprecate)
+
+ const mcpc_toolprop_t *
+ mcpc_tool_get_toolprop (const mcpc_tool_t *tool, size_t idx);
+
+`tool`: *tool*
+
+
+<a id="=mcpc_tool_get_toolprop_len="></a>
+
+## `mcpc_tool_get_toolprop_len`
+
+ size_t
+ mcpc_tool_get_toolprop_len (const mcpc_tool_t *tool);
+
+`tool`: *tool*
+
+
+<a id="=mcpc_tool_get_tpropval_i32="></a>
+
+## `mcpc_tool_get_tpropval_i32`
+
+ mcpc_errcode_t
+ mcpc_tool_get_tpropval_i32 (const mcpc_tool_t *tool, const char8_t *tprop_nament, int32_t *ret);
+
+`tool`: *tool*
+
+`tprop_nament`: *tool* property's name, null-terminated
+
+`ret`: the value of *tool* property
+
+
+<a id="=mcpc_tool_get_tpropval_u8str="></a>
+
+## `mcpc_tool_get_tpropval_u8str`
+
+ mcpc_errcode_t
+ mcpc_tool_get_tpropval_u8str (const mcpc_tool_t *tool, const char8_t *tprop_nament, char8_t *ret, size_t ret_cap, size_t *ret_len);
+
+`tool`: *tool*
+
+`tprop_nament`: *tool* property's name, null-terminated
+
+`ret`: the value of *tool* property
+
+
+<a id="=mcpc_tool_get_call_cb="></a>
+
+## `mcpc_tool_get_call_cb`
+
+ mcpc_errcode_t
+ mcpc_tool_get_call_cb (const mcpc_tool_t *tool, mcpc_tcallcb_t *ret);
+
+`tool`: *tool*
+
+`ret`: the callback for *tools/call*
+
+
+<a id="=mcpc_tool_set_call_cb="></a>
+
+## `mcpc_tool_set_call_cb`
+
+ mcpc_errcode_t
+ mcpc_tool_set_call_cb (mcpc_tool_t *tool, mcpc_tcallcb_t cb);
+
+`tool`: *tool*
+
+`ret`: the callback for *tools/call*
+
+
+<a id="=mcpc_toolcall_result_add_text_printf8="></a>
+
+## `mcpc_toolcall_result_add_text_printf8`
+
+Add a text content to the result of *tools/call*.
+
+Alias of `mcpc_ucbr_toolcall_add_text_printf8`.
+
+
+<a id="=mcpc_toolcall_result_add_errmsg_printf8="></a>
+
+## `mcpc_toolcall_result_add_errmsg_printf8`
+
+Add a error message to the result of *tools/call*. This will mark the
+whole result as an error.
+
+Alias of `mcpc_toolcall_result_add_errmsg_printf8`.
+
+
+<a id="org52ee5a7"></a>
+
+# Resource
+
+
+<a id="=mcpc_rsc_t="></a>
+
+## `mcpc_rsc_t`
+
+Resource
+
+
+<a id="=mcpc_rsc_new="></a>
+
+## `mcpc_rsc_new`
+
+ mcpc_rsc_t *
+ mcpc_rsc_new (const char8_t *uri, const size_t uri_len, const char8_t *name, const size_t name_len);
+
+`uri`: *Resource*'s URI
+
+`uri_len`: the length of *Resource*'s URI
+
+`name`: *Resource*'s name
+
+`name_len`: the length of *Resource*'s name
+
+
+<a id="=mcpc_rsc_new2="></a>
+
+## `mcpc_rsc_new2`
+
+ mcpc_rsc_t *
+ mcpc_rsc_new2 (const char8_t *uri_nt, const char8_t *nament);
+
+`uri_nt`: *Resource*'s URI, must be null-terminated
+
+`nament`: *Resource*'s name, must be null-terminated
+
+
+<a id="=mcpc_rsc_new3="></a>
+
+## `mcpc_rsc_new3`
+
+ mcpc_rsc_t *
+ mcpc_rsc_new3 (mcpc_mime_t mime, const char8_t *uri_nt, const char8_t *nament);
+
+`mime`: *Resource*'s MIME type
+
+`uri_nt`: *Resource*'s URI, must be null-terminated
+
+`nament`: *Resource*'s name, must be null-terminated
+
+
+<a id="=mcpc_rsc_free="></a>
+
+## `mcpc_rsc_free`
+
+ void
+ mcpc_rsc_free (mcpc_rsc_t *rsc);
+
+`rsc`: *Resource*
+
+
+<a id="=mcpc_rsc_get_uri="></a>
+
+## `mcpc_rsc_get_uri`
+
+ mcpc_errcode_t
+ mcpc_rsc_get_uri (const mcpc_rsc_t *rsc, char8_t *ret, size_t ret_cap, size_t *ret_len);
+
+`rsc`: *Resource*
+
+
+<a id="=mcpc_rsc_getref_uri="></a>
+
+## `mcpc_rsc_getref_uri`
+
+ mcpc_errcode_t
+ mcpc_rsc_getref_uri (const mcpc_rsc_t *rsc, const char8_t **ret, size_t *ret_len);
+
+`rsc`: *Resource*.
+
+`ret`: the pointer to *Resource*'s URI.
+
+`ret_len`: the length of *Resource*'s URI.
+
+
+<a id="=mcpc_rsc_getref_name="></a>
+
+## `mcpc_rsc_getref_name`
+
+ mcpc_errcode_t
+ mcpc_rsc_getref_name (const mcpc_rsc_t *rsc, const char8_t **ret, size_t *ret_len);
+
+`rsc`: *Resource*.
+
+`ret`: the pointer to *Resource*'s name.
+
+`ret_len`: the length of *Resource*'s name.
+
+
+<a id="=mcpc_rsc_get_mime="></a>
+
+## `mcpc_rsc_get_mime`
+
+ mcpc_errcode_t
+ mcpc_rsc_get_mime (const mcpc_rsc_t *rsc, mcpc_mime_t *ret);
+
+`rsc`: *Resource*.
+
+`ret`: *Resource*'s MIME type.
+
+
+<a id="org211140d"></a>
+
+# Prompt
+
+
+<a id="=mcpc_prmpt_t="></a>
+
+## `mcpc_prmpt_t`
+
+Prompt
+
+
+<a id="=mcpc_prmptarg_t="></a>
+
+## `mcpc_prmptarg_t`
+
+prompt argument
+
+
+<a id="=mcpc_prmptarghint_t="></a>
+
+## `mcpc_prmptarghint_t`
+
+prompt argument hint
+
+
+<a id="=mcpc_prmpt_callcb_t="></a>
+
+## `mcpc_prmpt_callcb_t`
+
+callback for prompts/get
+
+
+<a id="=mcpc_prmptarg_new="></a>
+
+## `mcpc_prmptarg_new`
+
+ mcpc_prmptarg_t *
+ mcpc_prmptarg_new (const char8_t *name, const size_t name_len, const char8_t *desc, const size_t desc_len);
+
+`name`: *Prompt* argument's name
+
+`name_len`: the length of *Prompt* argument's name
+
+`desc`: *Prompt* argument's description
+
+`desc_len`: the length of *Prompt* argument's description
+
+
+<a id="=mcpc_prmptarg_new2="></a>
+
+## `mcpc_prmptarg_new2`
+
+ mcpc_prmptarg_t *
+ mcpc_prmptarg_new2 (const char8_t *nament, const char8_t *descnt);
+
+`nament`: *Prompt* argument's name, must be null-terminated
+
+`descnt`: *Prompt* argument's description, must be null-terminated
+
+
+<a id="=mcpc_prmptarg_new3="></a>
+
+## `mcpc_prmptarg_new3`
+
+ mcpc_prmptarg_t *
+ mcpc_prmptarg_new3 (const char8_t *nament);
+
+`nament`: *Prompt* argument's name, must be null-terminated
+
+
+<a id="=mcpc_prmpt_new="></a>
+
+## `mcpc_prmpt_new`
+
+ mcpc_prmpt_t *
+ mcpc_prmpt_new (const char8_t *name, const size_t name_len, const char8_t *desc, const size_t desc_len);
+
+`name`: *Prompt*'s name
+
+`name_len`: the length of *Prompt*'s name
+
+`desc`: *Prompt*'s description
+
+`desc_len`: the length of *Prompt*'s description
+
+
+<a id="=mcpc_prmpt_new2="></a>
+
+## `mcpc_prmpt_new2`
+
+ mcpc_prmpt_t *
+ mcpc_prmpt_new2 (const char8_t *nament, const char8_t *descnt);
+
+`nament`: *Prompt*'s name, must be null-terminated
+
+`descnt`: *Prompt*'s description, must be null-terminated
+
+
+<a id="=mcpc_prmpt_new3="></a>
+
+## `mcpc_prmpt_new3`
+
+ mcpc_prmpt_t *
+ mcpc_prmpt_new3 (const char8_t *nament);
+
+`nament`: *Prompt*'s name, must be null-terminated
+
+
+<a id="=mcpc_prmpt_addfre_prmptarg="></a>
+
+## `mcpc_prmpt_addfre_prmptarg`
+
+ mcpc_errcode_t
+ mcpc_prmpt_addfre_prmptarg (mcpc_prmpt_t *prmpt, mcpc_prmptarg_t *prmptarg);
+
+`prmpt`: *Prompt*
+
+`prmptarg`: *Prompt* argument
+
+
+<a id="=mcpc_prmpt_addcpy_prmptarg="></a>
+
+## `mcpc_prmpt_addcpy_prmptarg`
+
+ void
+ mcpc_prmpt_addcpy_prmptarg (mcpc_prmpt_t *prmpt, mcpc_prmptarg_t *prmptarg);
+
+`prmpt`: *Prompt*
+
+`prmptarg`: *Prompt* argument
+
+
+<a id="=mcpc_prmpt_add_prmptarg="></a>
+
+## `mcpc_prmpt_add_prmptarg`
+
+Alias of `mcpc_prmpt_addfre_prmptarg`.
+
+
+<a id="=mcpc_prmpt_get_prmptarg="></a>
+
+## `mcpc_prmpt_get_prmptarg` (deprecated)
+
+ const mcpc_prmptarg_t *
+ mcpc_prmpt_get_prmptarg (const mcpc_prmpt_t *prmpt, size_t idx);
+
+`prmpt`: *Prompt*
+
+`idx`: &#x2026;
+
+
+<a id="=mcpc_prmpt_get_prmptarg_len="></a>
+
+## `mcpc_prmpt_get_prmptarg_len`
+
+ size_t
+ mcpc_prmpt_get_prmptarg_len (const mcpc_prmpt_t *prmpt);
+
+`prmpt`: *Prompt*
+
+
+<a id="=mcpc_prmpt_get_next_prmptarg="></a>
+
+## `mcpc_prmpt_get_next_prmptarg`
+
+ const mcpc_prmptarg_t *
+ mcpc_prmpt_get_next_prmptarg (const mcpc_prmpt_t *prmpt);
+
+`prmpt`: *Prompt*
+
+
+<a id="=mcpc_prmpt_getbyname_prmptarg="></a>
+
+## `mcpc_prmpt_getbyname_prmptarg`
+
+ const mcpc_prmptarg_t *
+ mcpc_prmpt_getbyname_prmptarg (const mcpc_prmpt_t *prmpt, const char8_t *parg_name, size_t parg_name_len);
+
+`prmpt`: *Prompt*
+
+`parg_name`: *Prompt* argument's name
+
+`parg_name_len`: the length of *Prompt* argument's name
+
+
+<a id="=mcpc_prmpt_getbynament_prmptarg="></a>
+
+## `mcpc_prmpt_getbynament_prmptarg`
+
+ const mcpc_prmptarg_t *
+ mcpc_prmpt_getbynament_prmptarg (const mcpc_prmpt_t *prmpt, const char8_t *parg_nament);
+
+`prmpt`: *Prompt*
+
+`parg_nament`: *Prompt* argument's name, must be null-terminated
+
+
+<a id="=mcpc_prmpt_get_callcb="></a>
+
+## `mcpc_prmpt_get_callcb`
+
+ mcpc_errcode_t
+ mcpc_prmpt_get_callcb (const mcpc_prmpt_t *prmpt, mcpc_prmpt_callcb_t *ret);
+
+`prmpt`: *Prompt*
+
+`ret`: the callback for *prompts/get*
+
+
+<a id="=mcpc_prmpt_set_callcb="></a>
+
+## `mcpc_prmpt_set_callcb`
+
+ mcpc_errcode_t
+ mcpc_prmpt_set_callcb (mcpc_prmpt_t *prmpt, mcpc_prmpt_callcb_t cb);
+
+`prmpt`: *prompt*
+
+`ret`: the callback for *prompts/get*
+
+
+<a id="=mcpc_prmptarg_get_u8str="></a>
+
+## `mcpc_prmptarg_get_u8str`
+
+ mcpc_errcode_t
+ mcpc_prmptarg_get_u8str (const mcpc_prmptarg_t *prmptarg, char8_t *ret, size_t ret_cap, size_t *ret_len);
+
+`prmptarg`: *prompt* argument
+
+`ret`: caller-provided buffer
+
+`ret_cap`: the buffer's capacity
+
+`ret_len`: the buffer's length
+
+
+<a id="=mcpc_prmptarg_getref_u8str="></a>
+
+## `mcpc_prmptarg_getref_u8str`
+
+ mcpc_errcode_t
+ mcpc_prmptarg_getref_u8str (const mcpc_prmptarg_t *prmptarg, const char8_t **ret, size_t *ret_len);
+
+`prmptarg`: *prompt* argument
+
+`ret`: the pointer to prompt's argument string data
+
+`ret_len`: the length of prompt's argument string data
+
+
+<a id="=mcpc_prmptarg_add_hint_printf8="></a>
+
+## `mcpc_prmptarg_add_hint_printf8`
+
+ mcpc_errcode_t
+ mcpc_prmptarg_add_hint_printf8 (mcpc_prmptarg_t *prmptarg, const char8_t *fmt, ...);
+
+`prmptarg`: *prompt* argument
+
+`fmt`: the format string to print
+
+
+<a id="=mcpc_prmptget_result_add_user_printf8="></a>
+
+## `mcpc_prmptget_result_add_user_printf8`
+
+Add a complete <span class="underline">user</span> prompt to the result of *prompts/get*.
+
+Alias of `mcpc_ucbr_prmptget_add_user_printf8`.
+
+
+<a id="=mcpc_prmptget_result_add_assist_printf8="></a>
+
+## `mcpc_prmptget_result_add_assist_printf8`
+
+Add a complete <span class="underline">assistant</span> prompt to the result of *prompts/get*.
+
+Alias of `mcpc_ucbr_prmptget_add_assist_printf8`.
+
+
+<a id="org6f84d9e"></a>
+
+# Others
+
+
+<a id="=mcpc_"></a>
+
+## `mcpc_errcode_t`
+
+Library-specific error codes.
+
+
+### Possible values
+
+`MCPC_EC_0`: success
+
+`MCPC_EC_BUG`: bug
+
+`MCPC_EC_BUFCAP`: insufficient buffer capacity
+
+`MCPC_EC_LONGTOOLNAME`: tool's name is too long
+
+`MCPC_EC_LONGTOOLDESC`: tool's description is too long
+
+`MCPC_EC_LONGTPROPNAME`: tool property's name is too long
+
+`MCPC_EC_LONGTPROPDESC`: tool property's description is too long
+
+`MCPC_EC_TOOLPROP_NOTFOUND`: tool property is not found
+
+`MCPC_EC_TOOLCALL_USERERR`: after tool calling, user indicates an error
+
+`MCPC_EC_CONNPOOL_NOTADD`: connection cannot be added
+
+`MCPC_EC_SVIMPL_SOCKET`: fail to create TCP socket
+
+`MCPC_EC_SVIMPL_BIND`: fail to bind TCP socket
+
+`MCPC_EC_SVIMPL_LISTEN`: fail to listen TCP socket
+
+`MCPC_EC_SVIMPL_TCP_INSUF_BUF`: insufficient buffer capacity
+
+`MCPC_EC_ANYDATA_NOTFOUND`: data cannot be found
+
+`MCPC_EC_ANYDATA_LONGNAME`: data's name is too long
+
+`MCPC_EC_PRMPTARG_NOTFOUND`: prompt agument cannot be found
+
+`MCPC_EC_JSON_FIELD_INVALID`: json field's value invalid, this means
+the field is missing, or field has unexpected type
+
+
+<a id="=mcpc_anytype_t="></a>
+
+## `mcpc_anytype_t`
+
+The data type recognizable by the library.
+
+
+### Possible values
+
+`MCPC_NONE`: nothing
+
+`MCPC_I8`: `int8_t`-like type
+
+`MCPC_U8`: `uint8_t`-like type
+
+`MCPC_I16`: `int16_t`-like type
+
+`MCPC_U16`: `uint16_t`-like type
+
+`MCPC_I32`: `int32_t`-like type
+
+`MCPC_U32`: `uint32_t`-like type
+
+`MCPC_I64`: `int64_t`-like type
+
+`MCPC_U64`: `uint64_t`-like type
+
+`MCPC_U8STR`: string of UTF-8 code units
+
+`MCPC_I8S`: an array of `int8_t`
+
+`MCPC_U8S`: an array of `uint8_t`
+
+`MCPC_I16S`: an array of `int16_t`
+
+`MCPC_U16S`: an array of `uint16_t`
+
+`MCPC_I32S`: an array of `int32_t`
+
+`MCPC_U32S`: an array of `uint32_t`
+
+`MCPC_I64S`: an array of `int64_t`
+
+`MCPC_U64S`: an array of `uint64_t`
+
+`MCPC_FUNC`: a function
+
+
+<a id="=mcpc_mime_t="></a>
+
+## `mcpc_mime_t`
+
+MIME types recognizable by the library, that conforms to [RFC2045](https://www.rfc-editor.org/rfc/rfc2045).
+
+
+### Possible value
+
+`MCPC_MIME_NONE`: nothing
+
+`MCPC_MIME_TEXT_PLAIN`: text/plain
+
+`MCPC_MIME_IMAGE_PNG`: image/png
+
+`MCPC_MIME_IMAGE_JPEG`: image/jpeg
+
+`MCPC_MIME_IMAGE_GIF`: image/gif
+
+`MCPC_MIME_AUDIO_MPEG`: audio/mpeg
+
+`MCPC_MIME_AUDIO_WAV`: audio/wav
+
+`MCPC_MIME_VIDEO_MPEG`: video/mpeg
+
+`MCPC_MIME_VIDEO_AVI`: video/avi
+
+`MCPC_MIME_APPLI_OCTS`: application/octet-stream
+
+`MCPC_MIME_APPLI_JSON`: application/json
+
+`MCPC_MIME_APPLI_PDF`: application/pdf
+
+
+<a id="=mcpc_ucbr_t="></a>
+
+## `mcpc_ucbr_t`
+
+User call back result.
+
+
+<a id="=mcpc_ucbr_toolcall_add_text_printf8="></a>
+
+## `mcpc_ucbr_toolcall_add_text_printf8`
+
+Add a text message to the result of *tools/call*.
+
+ void
+ mcpc_ucbr_toolcall_add_text_printf8 (mcpc_ucbr_t *ucbr, const char8_t *fmt, ...);
+
+`ucbr`: the callback result
+
+`fmt`: format string to be printed
+
+
+<a id="=mcpc_ucbr_toolcall_add_errmsg_printf8="></a>
+
+## `mcpc_ucbr_toolcall_add_errmsg_printf8`
+
+Add a error message to the result of *tools/call*.
+
+ void
+ mcpc_ucbr_toolcall_add_errmsg_printf8 (mcpc_ucbr_t *ucbr, const char8_t *fmt, ...);
+
+`ucbr`: the callback result
+
+`fmt`: format string to be printed
+
+
+<a id="=mcpc_ucbr_prmptget_add_user_printf8="></a>
+
+## `mcpc_ucbr_prmptget_add_user_printf8`
+
+Add a user prompt to the result of *prompts/get*.
+
+ void
+ mcpc_ucbr_prmptget_add_user_printf8 (mcpc_ucbr_t *ucbr, const char8_t *fmt, ...);
+
+`ucbr`: the callback result
+
+`fmt`: format string to be printed
+
+
+<a id="=mcpc_ucbr_prmptget_add_assist_printf8="></a>
+
+## `mcpc_ucbr_prmptget_add_assist_printf8`
+
+Add an assistant prompt to the result of *prompts/get*.
+
+ void
+ mcpc_ucbr_prmptget_add_assist_printf8 (mcpc_ucbr_t *ucbr, const char8_t *fmt, ...);
+
+`ucbr`: the callback result
+
+`fmt`: format string to be printed
+
diff --git a/third_party/mcpc/misc/api.org b/third_party/mcpc/misc/api.org
new file mode 100644
index 0000000..c3b33e5
--- /dev/null
+++ b/third_party/mcpc/misc/api.org
@@ -0,0 +1,1193 @@
+#+TITLE: API reference for mcpc
+#+OPTIONS: num:nil toc:2
+#+HTML_HEAD: <style> .outline-3 { border-top: 2px solid lightgray; } </style>
+
+
+* Server
+:PROPERTIES:
+:CUSTOM_ID: server
+:END:
+
+** =mcpc_server_t=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_server_t=
+:END:
+
+Server instance
+
+
+** =mcpc_server_new_iostrm=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_server_new_iostrm=
+:END:
+
+Create a I/O streams-base server.
+
+#+begin_src
+mcpc_server_t *
+mcpc_server_new_iostrm (const FILE *const strm_in, const FILE *const strm_out);
+#+end_src
+
+=strm_in=: input stream
+
+=strm_out=: output stream
+
+
+** =mcpc_server_new_tcp=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_server_new_tcp=
+:END:
+
+Create a TCP-based server.
+
+#+begin_src
+mcpc_server_t*
+mcpc_server_new_tcp ();
+#+end_src
+
+
+** =mcpc_server_set_nament=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_server_set_nament=
+:END:
+
+Set server's name.
+
+#+begin_src
+mcpc_errcode_t
+mcpc_server_set_nament (mcpc_server_t *sv, const char8_t *nament);
+#+end_src
+
+=sv=: server
+
+=nament=: null-terminated name. Must be a UTF-8 string
+
+
+** =mcpc_server_capa_enable_prmt=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_server_capa_enable_prmt=
+:END:
+
+Enable server's general capability of /Prompt/.
+
+#+begin_src
+mcpc_errcode_t
+mcpc_server_capa_enable_prmt (mcpc_server_t *sv);
+#+end_src
+
+=sv=: server
+
+
+** =mcpc_server_capa_enable_prmt_listchg=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_server_capa_enable_prmt_listchg=
+:END:
+
+Enable server's capability of notification for Prompt list updates.
+
+#+begin_src
+mcpc_errcode_t
+mcpc_server_capa_enable_prmt_listchg (mcpc_server_t *sv);
+#+end_src
+
+=sv=: server
+
+
+** =mcpc_server_capa_enable_tool=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_server_capa_enable_tool=
+:END:
+
+Enable server's general capability of /Prompt/.
+
+#+begin_src
+mcpc_errcode_t
+mcpc_server_capa_enable_tool (mcpc_server_t *sv);
+#+end_src
+
+=sv=: server
+
+
+** =mcpc_server_capa_enable_tool_listchg=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_server_capa_enable_tool_listchg=
+:END:
+
+Enable server's capability of notification for Prompt list updates.
+
+#+begin_src
+mcpc_errcode_t
+mcpc_server_capa_enable_tool_listchg (mcpc_server_t *sv);
+#+end_src
+
+=sv=: server
+
+
+** =mcpc_server_capa_enable_rsc=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_server_capa_enable_rsc=
+:END:
+
+Enable server's general capability of /Resource/.
+
+#+begin_src
+mcpc_errcode_t
+mcpc_server_capa_enable_rsc (mcpc_server_t *sv);
+#+end_src
+
+=sv=: server
+
+
+** =mcpc_server_capa_enable_rsc_subscr=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_server_capa_enable_rsc_subscr=
+:END:
+
+Enable server's capability of notification for Resource subscription.
+
+#+begin_src
+mcpc_errcode_t
+mcpc_server_capa_enable_rsc_subscr (mcpc_server_t *sv);
+#+end_src
+
+=sv=: server
+
+
+** =mcpc_server_capa_enable_rsc_listchg=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_server_capa_enable_rsc_listchg=
+:END:
+
+Enable server's capability of notification for Prompt list updates.
+
+#+begin_src
+mcpc_errcode_t
+mcpc_server_capa_enable_rsc_listchg (mcpc_server_t *sv);
+#+end_src
+
+=sv=: server
+
+
+** =mcpc_server_capa_enable_complt=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_server_capa_enable_complt=
+:END:
+
+Enable server's general capability of /Completion/.
+
+#+begin_src
+mcpc_errcode_t
+mcpc_server_capa_enable_complt (mcpc_server_t *sv);
+#+end_src
+
+=sv=: server
+
+
+** =mcpc_server_add_tool=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_server_add_tool=
+:END:
+
+Add a /Tool/ to a server.
+
+#+begin_src
+mcpc_errcode_t
+mcpc_server_add_tool (mcpc_server_t *sv, mcpc_tool_t *tool);
+#+end_src
+
+=sv=: server
+
+=tool=: tool, it will be automatically freed
+
+
+** =mcpc_server_add_prmpt=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_server_add_prmpt=
+:END:
+
+Add a /Prompt/ to a server.
+
+#+begin_src
+mcpc_errcode_t
+mcpc_server_add_prmpt (mcpc_server_t *sv, mcpc_prmpt_t *prmpt);
+#+end_src
+
+=sv=: server
+
+=prmpt=: prompt, it will be automatically freed
+
+
+** =mcpc_server_add_rsc=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_server_add_rsc=
+:END:
+
+Add a /Resource/ to a server.
+
+#+begin_src
+mcpc_errcode_t
+mcpc_server_add_rsc (mcpc_server_t *sv, mcpc_rsc_t *rsc);
+#+end_src
+
+=sv=: server
+
+=prmpt=: prompt, it will be automatically freed
+
+
+** =mcpc_server_start=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_server_start=
+:END:
+
+Start a server.
+
+#+begin_src
+mcpc_errcode_t
+mcpc_server_start (mcpc_server_t *sv);
+#+end_src
+
+=sv=: server
+
+
+** =mcpc_server_close=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_server_close=
+:END:
+
+Close a server.
+
+#+begin_src
+mcpc_errcode_t
+mcpc_server_close (mcpc_server_t *sv);
+#+end_src
+
+=sv=: server
+
+
+* Tool
+
+** =mcpc_tool_t=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_tool_t=
+:END:
+
+Tool
+
+
+** =mcpc_toolprop_t=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_toolprop_t=
+:END:
+
+Tool property
+
+
+** =mcpc_tcallcb_t=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_tcallcb_t=
+:END:
+
+The callback function of while doing tool calling.
+
+
+** =mcpc_toolprop_new=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_toolprop_new=
+:END:
+
+#+begin_src
+mcpc_toolprop_t *
+mcpc_toolprop_new (const char8_t *name, const size_t name_len, const char8_t *desc, const size_t desc_len, mcpc_anytype_t typ);
+#+end_src
+
+=name=: /tool/ property's name
+
+=name_len=: the length of /tool/ property's name
+
+=desc=: /tool/ property's description
+
+=desc_len=: the length of /tool/ property's description
+
+
+** =mcpc_toolprop_new2=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_toolprop_new2=
+:END:
+
+#+begin_src
+mcpc_toolprop_t *
+mcpc_toolprop_new2 (const char8_t *nament, const char8_t *descnt, mcpc_anytype_t typ);
+#+end_src
+
+=nament=: /tool/ property's name, must be null-terminated
+
+=descnt=: /tool/ property's description, must be null-terminated
+
+
+** =mcpc_tool_new=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_tool_new=
+:END:
+
+#+begin_src
+mcpc_tool_t *
+mcpc_tool_new (const char8_t *name, const size_t name_len, const char8_t *desc, const size_t desc_len);
+#+end_src
+
+=name=: /tool/'s name
+
+=name_len=: the length of /tool/'s name
+
+=desc=: /tool/'s description
+
+=desc_len=: the length of /tool/'s description
+
+
+** =mcpc_tool_new2=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_tool_new2=
+:END:
+
+#+begin_src
+mcpc_tool_t *
+mcpc_tool_new2 (const char8_t *nament, const char8_t *descnt);
+#+end_src
+
+=nament=: /tool/'s name, must be null-terminated
+
+=descnt=: /tool/'s description, must be null-terminated
+
+
+
+** =mcpc_tool_addfre_toolprop=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_tool_addfre_toolprop=
+:END:
+
+#+begin_src
+void
+mcpc_tool_addfre_toolprop (mcpc_tool_t *tool, mcpc_toolprop_t *toolprop);
+#+end_src
+
+=tool=: /tool/
+
+=toolprop=: /tool/ property
+
+
+** =mcpc_tool_addcpy_toolprop=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_tool_addcpy_toolprop=
+:END:
+
+#+begin_src
+void
+mcpc_tool_addcpy_toolprop (mcpc_tool_t *tool, mcpc_toolprop_t *toolprop);
+#+end_src
+
+=tool=: /tool/
+
+=toolprop=: /tool/ property
+
+
+
+** =mcpc_tool_add_toolprop=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_tool_add_toolprop=
+:END:
+
+Alias of =mcpc_tool_addfre_toolprop=
+
+
+** =mcpc_tool_get_toolprop= (deprecate)
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_tool_get_toolprop=
+:END:
+
+#+begin_src
+const mcpc_toolprop_t *
+mcpc_tool_get_toolprop (const mcpc_tool_t *tool, size_t idx);
+#+end_src
+
+=tool=: /tool/
+
+
+** =mcpc_tool_get_toolprop_len=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_tool_get_toolprop_len=
+:END:
+
+#+begin_src
+size_t
+mcpc_tool_get_toolprop_len (const mcpc_tool_t *tool);
+#+end_src
+
+=tool=: /tool/
+
+
+** =mcpc_tool_get_tpropval_i32=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_tool_get_tpropval_i32=
+:END:
+
+#+begin_src
+mcpc_errcode_t
+mcpc_tool_get_tpropval_i32 (const mcpc_tool_t *tool, const char8_t *tprop_nament, int32_t *ret);
+#+end_src
+
+=tool=: /tool/
+
+=tprop_nament=: /tool/ property's name, null-terminated
+
+=ret=: the value of /tool/ property
+
+
+** =mcpc_tool_get_tpropval_u8str=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_tool_get_tpropval_u8str=
+:END:
+
+#+begin_src
+mcpc_errcode_t
+mcpc_tool_get_tpropval_u8str (const mcpc_tool_t *tool, const char8_t *tprop_nament, char8_t *ret, size_t ret_cap, size_t *ret_len);
+#+end_src
+
+=tool=: /tool/
+
+=tprop_nament=: /tool/ property's name, null-terminated
+
+=ret=: the value of /tool/ property
+
+
+** =mcpc_tool_get_call_cb=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_tool_get_call_cb=
+:END:
+
+#+begin_src
+mcpc_errcode_t
+mcpc_tool_get_call_cb (const mcpc_tool_t *tool, mcpc_tcallcb_t *ret);
+#+end_src
+
+=tool=: /tool/
+
+=ret=: the callback for /tools/call/
+
+
+** =mcpc_tool_set_call_cb=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_tool_set_call_cb=
+:END:
+
+#+begin_src
+mcpc_errcode_t
+mcpc_tool_set_call_cb (mcpc_tool_t *tool, mcpc_tcallcb_t cb);
+#+end_src
+
+=tool=: /tool/
+
+=ret=: the callback for /tools/call/
+
+
+** =mcpc_toolcall_result_add_text_printf8=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_toolcall_result_add_text_printf8=
+:END:
+
+Add a text content to the result of /tools/call/.
+
+Alias of =mcpc_ucbr_toolcall_add_text_printf8=.
+
+
+** =mcpc_toolcall_result_add_errmsg_printf8=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_toolcall_result_add_errmsg_printf8=
+:END:
+
+Add a error message to the result of /tools/call/. This will mark the
+whole result as an error.
+
+Alias of =mcpc_toolcall_result_add_errmsg_printf8=.
+
+
+* Resource
+
+** =mcpc_rsc_t=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_rsc_t=
+:END:
+
+Resource
+
+
+** =mcpc_rsc_new=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_rsc_new=
+:END:
+
+#+begin_src
+mcpc_rsc_t *
+mcpc_rsc_new (const char8_t *uri, const size_t uri_len, const char8_t *name, const size_t name_len);
+#+end_src
+
+=uri=: /Resource/'s URI
+
+=uri_len=: the length of /Resource/'s URI
+
+=name=: /Resource/'s name
+
+=name_len=: the length of /Resource/'s name
+
+
+** =mcpc_rsc_new2=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_rsc_new2=
+:END:
+
+#+begin_src
+mcpc_rsc_t *
+mcpc_rsc_new2 (const char8_t *uri_nt, const char8_t *nament);
+#+end_src
+
+=uri_nt=: /Resource/'s URI, must be null-terminated
+
+=nament=: /Resource/'s name, must be null-terminated
+
+
+** =mcpc_rsc_new3=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_rsc_new3=
+:END:
+
+#+begin_src
+mcpc_rsc_t *
+mcpc_rsc_new3 (mcpc_mime_t mime, const char8_t *uri_nt, const char8_t *nament);
+#+end_src
+
+=mime=: /Resource/'s MIME type
+
+=uri_nt=: /Resource/'s URI, must be null-terminated
+
+=nament=: /Resource/'s name, must be null-terminated
+
+
+** =mcpc_rsc_free=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_rsc_free=
+:END:
+
+#+begin_src
+void
+mcpc_rsc_free (mcpc_rsc_t *rsc);
+#+end_src
+
+=rsc=: /Resource/
+
+
+** =mcpc_rsc_get_uri=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_rsc_get_uri=
+:END:
+
+#+begin_src
+mcpc_errcode_t
+mcpc_rsc_get_uri (const mcpc_rsc_t *rsc, char8_t *ret, size_t ret_cap, size_t *ret_len);
+#+end_src
+
+=rsc=: /Resource/
+
+
+** =mcpc_rsc_getref_uri=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_rsc_getref_uri=
+:END:
+
+#+begin_src
+mcpc_errcode_t
+mcpc_rsc_getref_uri (const mcpc_rsc_t *rsc, const char8_t **ret, size_t *ret_len);
+#+end_src
+
+=rsc=: /Resource/.
+
+=ret=: the pointer to /Resource/'s URI.
+
+=ret_len=: the length of /Resource/'s URI.
+
+
+** =mcpc_rsc_getref_name=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_rsc_getref_name=
+:END:
+
+#+begin_src
+mcpc_errcode_t
+mcpc_rsc_getref_name (const mcpc_rsc_t *rsc, const char8_t **ret, size_t *ret_len);
+#+end_src
+
+=rsc=: /Resource/.
+
+=ret=: the pointer to /Resource/'s name.
+
+=ret_len=: the length of /Resource/'s name.
+
+
+** =mcpc_rsc_get_mime=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_rsc_get_mime=
+:END:
+
+#+begin_src
+mcpc_errcode_t
+mcpc_rsc_get_mime (const mcpc_rsc_t *rsc, mcpc_mime_t *ret);
+#+end_src
+
+=rsc=: /Resource/.
+
+=ret=: /Resource/'s MIME type.
+
+
+* Prompt
+
+** =mcpc_prmpt_t=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmpt_t=
+:END:
+
+Prompt
+
+
+** =mcpc_prmptarg_t=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmptarg_t=
+:END:
+
+prompt argument
+
+
+** =mcpc_prmptarghint_t=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmptarghint_t=
+:END:
+
+prompt argument hint
+
+
+** =mcpc_prmpt_callcb_t=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmpt_callcb_t=
+:END:
+
+callback for prompts/get
+
+
+** =mcpc_prmptarg_new=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmptarg_new=
+:END:
+
+#+begin_src
+mcpc_prmptarg_t *
+mcpc_prmptarg_new (const char8_t *name, const size_t name_len, const char8_t *desc, const size_t desc_len);
+#+end_src
+
+=name=: /Prompt/ argument's name
+
+=name_len=: the length of /Prompt/ argument's name
+
+=desc=: /Prompt/ argument's description
+
+=desc_len=: the length of /Prompt/ argument's description
+
+
+** =mcpc_prmptarg_new2=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmptarg_new2=
+:END:
+
+#+begin_src
+mcpc_prmptarg_t *
+mcpc_prmptarg_new2 (const char8_t *nament, const char8_t *descnt);
+#+end_src
+
+=nament=: /Prompt/ argument's name, must be null-terminated
+
+=descnt=: /Prompt/ argument's description, must be null-terminated
+
+
+** =mcpc_prmptarg_new3=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmptarg_new3=
+:END:
+
+#+begin_src
+mcpc_prmptarg_t *
+mcpc_prmptarg_new3 (const char8_t *nament);
+#+end_src
+
+=nament=: /Prompt/ argument's name, must be null-terminated
+
+
+** =mcpc_prmpt_new=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmpt_new=
+:END:
+
+#+begin_src
+mcpc_prmpt_t *
+mcpc_prmpt_new (const char8_t *name, const size_t name_len, const char8_t *desc, const size_t desc_len);
+#+end_src
+
+=name=: /Prompt/'s name
+
+=name_len=: the length of /Prompt/'s name
+
+=desc=: /Prompt/'s description
+
+=desc_len=: the length of /Prompt/'s description
+
+
+** =mcpc_prmpt_new2=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmpt_new2=
+:END:
+
+#+begin_src
+mcpc_prmpt_t *
+mcpc_prmpt_new2 (const char8_t *nament, const char8_t *descnt);
+#+end_src
+
+=nament=: /Prompt/'s name, must be null-terminated
+
+=descnt=: /Prompt/'s description, must be null-terminated
+
+
+** =mcpc_prmpt_new3=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmpt_new3=
+:END:
+
+#+begin_src
+mcpc_prmpt_t *
+mcpc_prmpt_new3 (const char8_t *nament);
+#+end_src
+
+=nament=: /Prompt/'s name, must be null-terminated
+
+
+** =mcpc_prmpt_addfre_prmptarg=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmpt_addfre_prmptarg=
+:END:
+
+#+begin_src
+mcpc_errcode_t
+mcpc_prmpt_addfre_prmptarg (mcpc_prmpt_t *prmpt, mcpc_prmptarg_t *prmptarg);
+#+end_src
+
+=prmpt=: /Prompt/
+
+=prmptarg=: /Prompt/ argument
+
+
+** =mcpc_prmpt_addcpy_prmptarg=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmpt_addcpy_prmptarg=
+:END:
+
+#+begin_src
+void
+mcpc_prmpt_addcpy_prmptarg (mcpc_prmpt_t *prmpt, mcpc_prmptarg_t *prmptarg);
+#+end_src
+
+=prmpt=: /Prompt/
+
+=prmptarg=: /Prompt/ argument
+
+
+** =mcpc_prmpt_add_prmptarg=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmpt_add_prmptarg=
+:END:
+
+Alias of =mcpc_prmpt_addfre_prmptarg=.
+
+** =mcpc_prmpt_get_prmptarg= (deprecated)
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmpt_get_prmptarg=
+:END:
+
+#+begin_src
+const mcpc_prmptarg_t *
+mcpc_prmpt_get_prmptarg (const mcpc_prmpt_t *prmpt, size_t idx);
+#+end_src
+
+=prmpt=: /Prompt/
+
+=idx=: ...
+
+
+** =mcpc_prmpt_get_prmptarg_len=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmpt_get_prmptarg_len=
+:END:
+
+#+begin_src
+size_t
+mcpc_prmpt_get_prmptarg_len (const mcpc_prmpt_t *prmpt);
+#+end_src
+
+=prmpt=: /Prompt/
+
+
+** =mcpc_prmpt_get_next_prmptarg=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmpt_get_next_prmptarg=
+:END:
+
+#+begin_src
+const mcpc_prmptarg_t *
+mcpc_prmpt_get_next_prmptarg (const mcpc_prmpt_t *prmpt);
+#+end_src
+
+=prmpt=: /Prompt/
+
+
+** =mcpc_prmpt_getbyname_prmptarg=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmpt_getbyname_prmptarg=
+:END:
+
+#+begin_src
+const mcpc_prmptarg_t *
+mcpc_prmpt_getbyname_prmptarg (const mcpc_prmpt_t *prmpt, const char8_t *parg_name, size_t parg_name_len);
+#+end_src
+
+=prmpt=: /Prompt/
+
+=parg_name=: /Prompt/ argument's name
+
+=parg_name_len=: the length of /Prompt/ argument's name
+
+
+** =mcpc_prmpt_getbynament_prmptarg=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmpt_getbynament_prmptarg=
+:END:
+
+#+begin_src
+const mcpc_prmptarg_t *
+mcpc_prmpt_getbynament_prmptarg (const mcpc_prmpt_t *prmpt, const char8_t *parg_nament);
+#+end_src
+
+=prmpt=: /Prompt/
+
+=parg_nament=: /Prompt/ argument's name, must be null-terminated
+
+
+** =mcpc_prmpt_get_callcb=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmpt_get_callcb=
+:END:
+
+#+begin_src
+mcpc_errcode_t
+mcpc_prmpt_get_callcb (const mcpc_prmpt_t *prmpt, mcpc_prmpt_callcb_t *ret);
+#+end_src
+
+=prmpt=: /Prompt/
+
+=ret=: the callback for /prompts/get/
+
+
+** =mcpc_prmpt_set_callcb=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmpt_set_callcb=
+:END:
+
+#+begin_src
+mcpc_errcode_t
+mcpc_prmpt_set_callcb (mcpc_prmpt_t *prmpt, mcpc_prmpt_callcb_t cb);
+#+end_src
+
+=prmpt=: /prompt/
+
+=ret=: the callback for /prompts/get/
+
+
+** =mcpc_prmptarg_get_u8str=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmptarg_get_u8str=
+:END:
+
+#+begin_src
+mcpc_errcode_t
+mcpc_prmptarg_get_u8str (const mcpc_prmptarg_t *prmptarg, char8_t *ret, size_t ret_cap, size_t *ret_len);
+#+end_src
+
+=prmptarg=: /prompt/ argument
+
+=ret=: caller-provided buffer
+
+=ret_cap=: the buffer's capacity
+
+=ret_len=: the buffer's length
+
+
+** =mcpc_prmptarg_getref_u8str=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmptarg_getref_u8str=
+:END:
+
+#+begin_src
+mcpc_errcode_t
+mcpc_prmptarg_getref_u8str (const mcpc_prmptarg_t *prmptarg, const char8_t **ret, size_t *ret_len);
+#+end_src
+
+=prmptarg=: /prompt/ argument
+
+=ret=: the pointer to prompt's argument string data
+
+=ret_len=: the length of prompt's argument string data
+
+
+** =mcpc_prmptarg_add_hint_printf8=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmptarg_add_hint_printf8=
+:END:
+
+#+begin_src
+mcpc_errcode_t
+mcpc_prmptarg_add_hint_printf8 (mcpc_prmptarg_t *prmptarg, const char8_t *fmt, ...);
+#+end_src
+
+=prmptarg=: /prompt/ argument
+
+=fmt=: the format string to print
+
+
+** =mcpc_prmptget_result_add_user_printf8=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmptget_result_add_user_printf8=
+:END:
+
+Add a complete _user_ prompt to the result of /prompts/get/.
+
+Alias of =mcpc_ucbr_prmptget_add_user_printf8=.
+
+
+** =mcpc_prmptget_result_add_assist_printf8=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_prmptget_result_add_assist_printf8=
+:END:
+
+
+Add a complete _assistant_ prompt to the result of /prompts/get/.
+
+Alias of =mcpc_ucbr_prmptget_add_assist_printf8=.
+
+
+* Others
+
+** =mcpc_errcode_t=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_
+:END:
+
+Library-specific error codes.
+
+*** Possible values
+
+=MCPC_EC_0=: success
+
+=MCPC_EC_BUG=: bug
+
+=MCPC_EC_BUFCAP=: insufficient buffer capacity
+
+=MCPC_EC_LONGTOOLNAME=: tool's name is too long
+
+=MCPC_EC_LONGTOOLDESC=: tool's description is too long
+
+=MCPC_EC_LONGTPROPNAME=: tool property's name is too long
+
+=MCPC_EC_LONGTPROPDESC=: tool property's description is too long
+
+=MCPC_EC_TOOLPROP_NOTFOUND=: tool property is not found
+
+=MCPC_EC_TOOLCALL_USERERR=: after tool calling, user indicates an error
+
+=MCPC_EC_CONNPOOL_NOTADD=: connection cannot be added
+
+=MCPC_EC_SVIMPL_SOCKET=: fail to create TCP socket
+
+=MCPC_EC_SVIMPL_BIND=: fail to bind TCP socket
+
+=MCPC_EC_SVIMPL_LISTEN=: fail to listen TCP socket
+
+=MCPC_EC_SVIMPL_TCP_INSUF_BUF=: insufficient buffer capacity
+
+=MCPC_EC_ANYDATA_NOTFOUND=: data cannot be found
+
+=MCPC_EC_ANYDATA_LONGNAME=: data's name is too long
+
+=MCPC_EC_PRMPTARG_NOTFOUND=: prompt agument cannot be found
+
+=MCPC_EC_JSON_FIELD_INVALID=: json field's value invalid, this means
+the field is missing, or field has unexpected type
+
+
+** =mcpc_anytype_t=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_anytype_t=
+:END:
+
+The data type recognizable by the library.
+
+*** Possible values
+
+=MCPC_NONE=: nothing
+
+=MCPC_I8=: =int8_t=-like type
+
+=MCPC_U8=: =uint8_t=-like type
+
+=MCPC_I16=: =int16_t=-like type
+
+=MCPC_U16=: =uint16_t=-like type
+
+=MCPC_I32=: =int32_t=-like type
+
+=MCPC_U32=: =uint32_t=-like type
+
+=MCPC_I64=: =int64_t=-like type
+
+=MCPC_U64=: =uint64_t=-like type
+
+=MCPC_U8STR=: string of UTF-8 code units
+
+=MCPC_I8S=: an array of =int8_t=
+
+=MCPC_U8S=: an array of =uint8_t=
+
+=MCPC_I16S=: an array of =int16_t=
+
+=MCPC_U16S=: an array of =uint16_t=
+
+=MCPC_I32S=: an array of =int32_t=
+
+=MCPC_U32S=: an array of =uint32_t=
+
+=MCPC_I64S=: an array of =int64_t=
+
+=MCPC_U64S=: an array of =uint64_t=
+
+=MCPC_FUNC=: a function
+
+
+** =mcpc_mime_t=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_mime_t=
+:END:
+
+MIME types recognizable by the library, that conforms to [[https://www.rfc-editor.org/rfc/rfc2045][RFC2045]].
+
+*** Possible value
+
+=MCPC_MIME_NONE=: nothing
+
+=MCPC_MIME_TEXT_PLAIN=: text/plain
+
+=MCPC_MIME_IMAGE_PNG=: image/png
+
+=MCPC_MIME_IMAGE_JPEG=: image/jpeg
+
+=MCPC_MIME_IMAGE_GIF=: image/gif
+
+=MCPC_MIME_AUDIO_MPEG=: audio/mpeg
+
+=MCPC_MIME_AUDIO_WAV=: audio/wav
+
+=MCPC_MIME_VIDEO_MPEG=: video/mpeg
+
+=MCPC_MIME_VIDEO_AVI=: video/avi
+
+=MCPC_MIME_APPLI_OCTS=: application/octet-stream
+
+=MCPC_MIME_APPLI_JSON=: application/json
+
+=MCPC_MIME_APPLI_PDF=: application/pdf
+
+
+** =mcpc_ucbr_t=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_ucbr_t=
+:END:
+
+User call back result.
+
+
+** =mcpc_ucbr_toolcall_add_text_printf8=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_ucbr_toolcall_add_text_printf8=
+:END:
+
+Add a text message to the result of /tools/call/.
+
+#+begin_src
+void
+mcpc_ucbr_toolcall_add_text_printf8 (mcpc_ucbr_t *ucbr, const char8_t *fmt, ...);
+#+end_src
+
+=ucbr=: the callback result
+
+=fmt=: format string to be printed
+
+
+** =mcpc_ucbr_toolcall_add_errmsg_printf8=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_ucbr_toolcall_add_errmsg_printf8=
+:END:
+
+Add a error message to the result of /tools/call/.
+
+#+begin_src
+void
+mcpc_ucbr_toolcall_add_errmsg_printf8 (mcpc_ucbr_t *ucbr, const char8_t *fmt, ...);
+#+end_src
+
+=ucbr=: the callback result
+
+=fmt=: format string to be printed
+
+
+** =mcpc_ucbr_prmptget_add_user_printf8=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_ucbr_prmptget_add_user_printf8=
+:END:
+
+Add a user prompt to the result of /prompts/get/.
+
+#+begin_src
+void
+mcpc_ucbr_prmptget_add_user_printf8 (mcpc_ucbr_t *ucbr, const char8_t *fmt, ...);
+#+end_src
+
+=ucbr=: the callback result
+
+=fmt=: format string to be printed
+
+
+** =mcpc_ucbr_prmptget_add_assist_printf8=
+:PROPERTIES:
+:CUSTOM_ID: =mcpc_ucbr_prmptget_add_assist_printf8=
+:END:
+
+Add an assistant prompt to the result of /prompts/get/.
+
+#+begin_src
+void
+mcpc_ucbr_prmptget_add_assist_printf8 (mcpc_ucbr_t *ucbr, const char8_t *fmt, ...);
+#+end_src
+
+=ucbr=: the callback result
+
+=fmt=: format string to be printed
+
diff --git a/third_party/mcpc/misc/init-msvc.bat b/third_party/mcpc/misc/init-msvc.bat
new file mode 100644
index 0000000..46b64a5
--- /dev/null
+++ b/third_party/mcpc/misc/init-msvc.bat
@@ -0,0 +1,5 @@
+
+"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"
+
+@REM previous
+@REM "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\VsDevCmd.bat"
diff --git a/third_party/mcpc/mk/ccdefs-cygwin.mk b/third_party/mcpc/mk/ccdefs-cygwin.mk
new file mode 100644
index 0000000..d41e814
--- /dev/null
+++ b/third_party/mcpc/mk/ccdefs-cygwin.mk
@@ -0,0 +1,34 @@
+ifneq (,$(and $(isnot_done_ccdefs),$(is_cygwin)))
+ is_unix = xxx
+ is_unixar = xxx
+ is_winld = xxx
+
+ # currently we enforce gcc on cygwin
+ CC = gcc
+ CFLAGS += -Dis_unix
+ CFLAGS += -Dis_cygwin
+ is_gcc = xxx
+ is_gcclike = xxx
+ gcc_ver = $(shell $(CC) --version)
+ is_gcc12 = $(findstring 12.,$(gcc_ver))
+ is_gcc13 = $(findstring 13.,$(gcc_ver))
+ is_gcc14 = $(findstring 14.,$(gcc_ver))
+
+ ifeq (1,2)
+ else ifneq (,$(and $(is_gcc12)))
+ # deb12
+ CFLAGS += -std=c17
+ CFLAGS += -DMCPC_C23PTCH_KW1
+ CFLAGS += -DMCPC_C23PTCH_CKD1
+ CFLAGS += -DMCPC_C23PTCH_UCHAR1
+ isnot_done_ccdefs =
+ else ifneq (,$(and $(is_gcc14)))
+ # fc41
+ CFLAGS += -std=c23
+ CFLAGS += -DMCPC_C23PTCH_UCHAR2
+ isnot_done_ccdefs =
+ endif
+
+
+
+endif
diff --git a/third_party/mcpc/mk/ccdefs-linux.mk b/third_party/mcpc/mk/ccdefs-linux.mk
new file mode 100644
index 0000000..b59540a
--- /dev/null
+++ b/third_party/mcpc/mk/ccdefs-linux.mk
@@ -0,0 +1,79 @@
+ifneq (,$(and $(isnot_done_ccdefs),$(is_linux)))
+ is_unix = xxx
+ is_unixar = xxx
+ CFLAGS += -Dis_unix
+
+ cc_ver = $(shell $(CC) --version)
+ ifneq (,$(findstring Free Software Foundation,$(cc_ver)))
+ found_gcc = xxx
+ endif
+ ifneq (,$(findstring clang,$(cc_ver)))
+ found_clang = xxx
+ endif
+
+ ifneq (,$(found_gcc))
+ is_gcc = xxx
+ is_gcclike = xxx
+ gcc_ver = $(shell $(CC) --version)
+ is_gcc10 = $(findstring 10.,$(gcc_ver))
+ is_gcc12 = $(findstring 12.,$(gcc_ver))
+ is_gcc13 = $(findstring 13.,$(gcc_ver))
+ is_gcc14 = $(findstring 14.,$(gcc_ver))
+ else ifneq (,$(found_clang))
+ is_clang = xxx
+ is_gcclike = xxx
+ clang_ver = $(shell $(CC) --version)
+ is_clang18 = $(findstring 18.,$(clang_ver))
+ is_clang19 = $(findstring 19.,$(clang_ver))
+ endif
+
+ ldd_ver = $(shell ldd --version)
+ is_glibc231 = $(and $(findstring Free Software Foundation,$(ldd_ver)),$(findstring 2.31,$(ldd_ver)))
+ is_glibc236 = $(and $(findstring Free Software Foundation,$(ldd_ver)),$(findstring 2.36,$(ldd_ver)))
+ is_glibc240 = $(and $(findstring Free Software Foundation,$(ldd_ver)),$(findstring 2.40,$(ldd_ver)))
+ is_cygwin = $(and $(findstring cygwin,$(ldd_ver)))
+
+ ifeq (1,2)
+ else ifneq (,$(and $(is_gcc10),$(is_glibc231)))
+ # deb11
+ CFLAGS += -std=c17
+ CFLAGS += -DMCPC_C23PTCH_KW1
+ CFLAGS += -DMCPC_C23PTCH_CKD1
+ CFLAGS += -DMCPC_C23PTCH_UCHAR1
+ CFLAGS += -DMCPC_C23GIVUP_FIXENUM
+ is_manual_pthread = xxx
+ isnot_done_ccdefs =
+ else ifneq (,$(and $(is_gcc12),$(is_glibc236)))
+ # deb12
+ CFLAGS += -std=c17
+ CFLAGS += -DMCPC_C23PTCH_KW1
+ CFLAGS += -DMCPC_C23PTCH_CKD1
+ CFLAGS += -DMCPC_C23PTCH_UCHAR1
+ CFLAGS += -DMCPC_C23GIVUP_FIXENUM
+ isnot_done_ccdefs =
+ else ifneq (,$(and $(is_gcc14),$(is_cygwin)))
+ # fc41
+ CFLAGS += -std=c23
+ CFLAGS += -DMCPC_C23PTCH_UCHAR1
+ isnot_done_ccdefs =
+ else ifneq (,$(and $(is_gcc14),$(is_mingw)))
+ # fc41
+ CFLAGS += -std=c233
+ isnot_done_ccdefs =
+ else ifneq (,$(and $(is_gcc14),$(is_glibc240)))
+ # fc41
+ CFLAGS += -std=c23
+ isnot_done_ccdefs =
+ else ifneq (,$(and $(is_clang18),$(is_glibc240)))
+ # fc41
+ CFLAGS += -std=c23
+ isnot_done_ccdefs =
+ else ifneq (,$(and $(is_clang19),$(is_glibc240)))
+ # fc41
+ CFLAGS += -std=c23
+ isnot_done_ccdefs =
+ endif
+
+
+
+endif
diff --git a/third_party/mcpc/mk/ccdefs-macos.mk b/third_party/mcpc/mk/ccdefs-macos.mk
new file mode 100644
index 0000000..8e38be5
--- /dev/null
+++ b/third_party/mcpc/mk/ccdefs-macos.mk
@@ -0,0 +1,53 @@
+ifneq (,$(and $(isnot_done_ccdefs),$(is_macos)))
+ is_unix = xxx
+ is_unixar = xxx
+ CFLAGS += -Dis_unix
+ cc_ver = $(shell $(CC) --version)
+ ifneq (,$(findstring Free Software Foundation,$(cc_ver)))
+ found_gcc = xxx
+ endif
+ ifneq (,$(findstring clang,$(cc_ver)))
+ found_clang = xxx
+ endif
+
+ ifneq (,$(found_gcc))
+ is_gcc = xxx
+ is_gcclike = xxx
+ gcc_ver = $(shell $(CC) --version)
+ is_gcc12 = $(findstring 12.,$(gcc_ver))
+ is_gcc13 = $(findstring 13.,$(gcc_ver))
+ is_gcc14 = $(findstring 14.,$(gcc_ver))
+ else ifneq (,$(found_clang))
+ is_clang = xxx
+ is_gcclike = xxx
+ clang_ver = $(shell $(CC) --version)
+ is_clang14 = $(findstring 14.,$(clang_ver))
+ is_clang15 = $(findstring 15.,$(clang_ver))
+ is_clang16 = $(findstring 16.,$(clang_ver))
+ endif
+
+ ifeq (1,2)
+ else ifneq (,$(and $(is_clang14)))
+ CFLAGS += -std=c17
+ CFLAGS += -DMCPC_C23PTCH_KW1
+ CFLAGS += -DMCPC_C23PTCH_CKD1
+ CFLAGS += -DMCPC_C23PTCH_UCHAR1
+ CFLAGS += -Dno_c23_n2508
+ isnot_done_ccdefs =
+ else ifneq (,$(and $(is_clang15)))
+ CFLAGS += -std=c17
+ CFLAGS += -DMCPC_C23PTCH_KW1
+ CFLAGS += -DMCPC_C23PTCH_CKD1
+ CFLAGS += -DMCPC_C23PTCH_UCHAR1
+ CFLAGS += -Dno_c23_n2508
+ isnot_done_ccdefs =
+ else ifneq (,$(and $(is_clang16)))
+ CFLAGS += -std=c17
+ CFLAGS += -DMCPC_C23PTCH_KW1
+ CFLAGS += -DMCPC_C23PTCH_CKD1
+ CFLAGS += -DMCPC_C23PTCH_UCHAR1
+ CFLAGS += -Dno_c23_n2508
+ isnot_done_ccdefs =
+ endif
+
+endif
diff --git a/third_party/mcpc/mk/ccdefs-mingw.mk b/third_party/mcpc/mk/ccdefs-mingw.mk
new file mode 100644
index 0000000..4b36d0b
--- /dev/null
+++ b/third_party/mcpc/mk/ccdefs-mingw.mk
@@ -0,0 +1,89 @@
+ifneq (,$(and $(isnot_done_ccdefs),$(is_mingw)))
+ is_unix = xxx
+ CFLAGS += -Dis_mingw
+ CFLAGS += -Dis_win # winapi?
+
+ wherecl = $(shell where cl)
+ ifneq (,$(wherecl))
+ found_cl = xxx
+ endif
+
+ # Use cl whenever possible
+ ifneq (,$(and $(found_cl),$(is_indirsh)))
+ CC = cl
+ is_cllike = xxx
+ is_winlib = xxx
+ else
+ # To find any other cc
+
+ # gh win2019 patch, intentionally to use gcc
+ ifneq (,$(is_win2019))
+ CC = gcc
+ endif
+ # GH win2022 patch, intentionally to use gcc
+ ifneq (,$(is_win2022))
+ CC = gcc
+ endif
+
+ cc_ver = $(shell $(CC) --version)
+ ifneq (,$(findstring Free Software Foundation,$(cc_ver)))
+ found_gcc = xxx
+ endif
+ ifneq (,$(found_gcc))
+ is_gcc = xxx
+ is_gcclike = xxx
+ is_unixar = xxx
+ is_winld = xxx
+ gcc_ver = $(shell $(CC) --version)
+ is_gcc8 = $(findstring 8.,$(gcc_ver))
+ is_gcc12 = $(findstring 12.,$(gcc_ver))
+ is_gcc13 = $(findstring 13.,$(gcc_ver))
+ is_gcc14 = $(findstring 14.,$(gcc_ver))
+ endif
+ ifneq (,$(found_clang))
+ is_clang = xxx
+ is_gcclike = xxx
+ is_unixar = xxx
+ is_winld = xxx
+ clang_ver = $(shell $(CC) --version)
+ is_clang18 = $(findstring 18.,$(clang_ver))
+ is_clang19 = $(findstring 19.,$(clang_ver))
+ endif
+ endif
+
+ ifeq (1,2)
+ else ifneq (,$(is_cllike))
+ CFLAGS += /Dis_win
+ CFLAGS += /Dis_wincl
+ CFLAGS += /nologo
+ CFLAGS += /std:c17
+ CFLAGS += /DMCPC_C23PTCH_KW1
+ CFLAGS += /DMCPC_C23PTCH_CKD1
+ CFLAGS += /DMCPC_C23PTCH_UCHAR1
+ CFLAGS += /DMCPC_C23GIVUP_FIXENUM
+ LDFLAGS += /nologo
+ is_cllike = xxx
+ is_winlib = xxx
+ is_cl = xxx
+ isnot_done_ccdefs =
+ else ifneq (,$(and $(is_gcc8)))
+ CFLAGS += -std=c11
+ CFLAGS += -DMCPC_C23PTCH_KW1
+ CFLAGS += -DMCPC_C23PTCH_CKD1
+ CFLAGS += -DMCPC_C23PTCH_UCHAR1
+ CFLAGS += -DMCPC_C23GIVUP_FIXENUM
+ isnot_done_ccdefs =
+ else ifneq (,$(and $(is_gcc12)))
+ CFLAGS += -std=c17
+ CFLAGS += -DMCPC_C23PTCH_KW1
+ CFLAGS += -DMCPC_C23PTCH_CKD1
+ CFLAGS += -DMCPC_C23PTCH_UCHAR1
+ CFLAGS += -DMCPC_C23GIVUP_FIXENUM
+ isnot_done_ccdefs =
+ else ifneq (,$(and $(is_gcc14)))
+ # fc41
+ CFLAGS += -std=c23
+ CFLAGS += -DMCPC_C23PTCH_UCHAR2
+ isnot_done_ccdefs =
+ endif
+endif
diff --git a/third_party/mcpc/mk/ccdefs-win.mk b/third_party/mcpc/mk/ccdefs-win.mk
new file mode 100644
index 0000000..76e9246
--- /dev/null
+++ b/third_party/mcpc/mk/ccdefs-win.mk
@@ -0,0 +1,60 @@
+ifneq (,$(and $(isnot_done_ccdefs),$(is_win)))
+
+ ifdef USE_WINCLANG
+ CC = clang-cl
+ CFLAGS += /Dis_win
+ is_cllike = xxx
+ is_clang = xxx
+ is_winclang = xxx
+ clang_ver = $(shell $(CC) --version)
+ is_clang14 = $(findstring 14.,$(clang_ver))
+ is_clang16 = $(findstring 16.,$(clang_ver))
+ is_clang19 = $(findstring 19.,$(clang_ver))
+
+ ifeq (1,2)
+ else ifneq (,$(and $(is_clang14)))
+ CFLAGS += /std:c17
+ CFLAGS += -DMCPC_C23PTCH_KW1
+ CFLAGS += -DMCPC_C23PTCH_CKD1
+ CFLAGS += -DMCPC_C23PTCH_UCHAR1
+ else ifneq (,$(and $(is_clang16)))
+ CFLAGS += /std:c17
+ CFLAGS += -DMCPC_C23PTCH_KW1
+ CFLAGS += -DMCPC_C23PTCH_CKD1
+ CFLAGS += -DMCPC_C23PTCH_UCHAR1
+ else ifneq (,$(and $(is_clang19)))
+ CFLAGS += /std:c17
+ CFLAGS += -DMCPC_C23PTCH_KW1
+ CFLAGS += -DMCPC_C23PTCH_CKD1
+ CFLAGS += -DMCPC_C23PTCH_UCHAR1
+ endif
+
+ else
+
+ ifneq (,$(found_cl))
+ CC = cl
+ CFLAGS += /Dis_win
+ CFLAGS += /Dis_wincl
+ CFLAGS += /nologo
+ CFLAGS += /std:c17
+ CFLAGS += /DMCPC_C23PTCH_KW1
+ CFLAGS += /DMCPC_C23PTCH_CKD1
+ CFLAGS += /DMCPC_C23PTCH_UCHAR1
+ CFLAGS += /DMCPC_C23GIVUP_FIXENUM
+ LDFLAGS += /nologo
+ is_cllike = xxx
+ is_winlib = xxx
+ is_cl = xxx
+ isnot_done_ccdefs =
+ else
+ cc_ver = $(shell $(CC) --version)
+ ifneq (,$(findstring Free Software Foundation,$(ccver)))
+ is_gcc = xxx
+ is_gcclike = xxx
+ is_unixar = xxx
+ is_winld = xxx
+ endif
+ endif
+ endif
+
+endif
diff --git a/third_party/mcpc/mk/dbgrel.mk b/third_party/mcpc/mk/dbgrel.mk
new file mode 100644
index 0000000..a32e0bd
--- /dev/null
+++ b/third_party/mcpc/mk/dbgrel.mk
@@ -0,0 +1,92 @@
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# dbgrel
+
+ifdef DBG
+ ifeq (1,2)
+ else ifneq (,$(is_gcclike))
+ CFLAGS += -g -O0
+ CFLAGS += -Wall -Wextra -Werror -Wno-unused-function -Wno-unused-parameter -Wno-unused-label
+ CFLAGS += -DMCPC_DBG
+ CFLAGS += -I..
+ else ifneq (,$(is_cllike))
+ CFLAGS += /Zi /Od /Ob0
+ CFLAGS += /RTC1
+ # CFLAGS += /Wall
+ CFLAGS += /DMCPC_DBG
+ CFLAGS += /I..
+ endif
+else
+ ifeq (1,2)
+ else ifneq (,$(is_gcclike))
+ CFLAGS += -Wall -Wextra -Werror -Wno-unused-function -Wno-unused-parameter -Wno-unused-label -Wno-error=unused-variable -Wno-error=unused-but-set-variable
+ CFLAGS += -O2 -Os
+ CFLAGS += -I..
+ else ifneq (,$(is_cllike))
+ CFLAGS += /MD
+ CFLAGS += /Wall
+ CFLAGS += /wd4152 # okay with: nonstandard extension, function/data pointer conversion in expression
+ CFLAGS += /wd4310 # okay with cast causing truncate
+ CFLAGS += /wd4127 # window is wrong about sizeof(long) being constant
+ CFLAGS += /wd4102 # okay to have unref label
+ CFLAGS += /wd4189 # local variable is initialized but not referenced
+ CFLAGS += /wd4100 # its okay not ref to param
+ CFLAGS += /wd5045 # why call spectre rather than boundcheck?
+ CFLAGS += /wd4005 # alloca macro redef is ok
+ CFLAGS += /wd4996 # use strerror sprintf anyway
+ CFLAGS += /wd4456 # no need to warn shadow decl
+ CFLAGS += /wd4255 # ok for C23
+ CFLAGS += /wd4668 # disb #if macro
+ CFLAGS += /wd4710 # disable inline warn
+ CFLAGS += /wd4820 # we know there is padding
+ CFLAGS += /O2 /Ob1
+ CFLAGS += /I..
+ endif
+endif
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# strip
+
+ifndef DBG
+ # LDFLAGS += --strip-all ld not recoginizing?
+ ifneq (,$(is_linux))
+ LDFLAGS += -s
+ endif
+ ifneq (,$(is_macos))
+ LDFLAGS += -dead_strip
+ endif
+endif
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# sanitizer
+
+ifdef VALG
+ which_valg = $(shell which valgrind)
+ ifneq (,$(which_valg))
+ is_valg = xxx
+ endif
+endif
+
+
+# NOTE asan triggered only if ASAN=1, we use valgrind by default
+ifdef ASAN
+ is_asan = xxx
+ is_valg =
+ ifneq (,$(is_linux))
+ CFLAGS += -fsanitize=address
+ LDFLAGS += -lasan
+ endif
+ ifneq (,$(is_macos))
+ CFLAGS += -fsanitize=address
+ LDFLAGS += -fsanitize=address
+ endif
+ ifneq (,$(is_cllike))
+ CFLAGS += /fsanitize=address
+ LDFLAGS += /DEBUG
+# LDFLAGS += "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\Llvm\\lib\\clang\\19\\lib\\windows\\clang_rt.asan-i386.lib"
+ LDFLAGS += /NODEFAULTLIB:libcmt.lib
+ LDFLAGS += msvcrt.lib
+ endif
+endif
+
+
diff --git a/third_party/mcpc/mk/step0.mk b/third_party/mcpc/mk/step0.mk
new file mode 100644
index 0000000..8ec5d72
--- /dev/null
+++ b/third_party/mcpc/mk/step0.mk
@@ -0,0 +1,31 @@
+ifeq ($(SHELL),/bin/sh)
+ is_sh = xxx
+ is_dirsh = xxx
+ unameall = $(shell uname -a)
+ is_linux = $(findstring Linux,$(unameall))
+ is_cygwin = $(findstring Cygwin,$(unameall))
+ is_mingw = $(findstring MINGW64_NT,$(unameall))
+ is_macos = $(findstring Darwin,$(unameall))
+else ifneq (,$(findstring /sh.exe,$(SHELL)))
+ # indirectly enter bash (but the only way we enter GH win)
+ is_sh = xxx
+ is_indirsh = xxx
+ is_win = xxx
+ unameall = $(shell uname -a)
+ is_msys = $(findstring MSYS_NT,$(unameall))
+ is_mingw = $(findstring MINGW64_NT,$(unameall))
+ sysinfo = $(shell systeminfo)
+ ifneq (,$(findstring Windows Server 2019,$(sysinfo)))
+ is_win2019 = xxx
+ endif
+ ifneq (,$(findstring Windows Server 2022,$(sysinfo)))
+ is_win2022 = xxx
+ endif
+ ifneq (,$(findstring Windows Server 2025,$(sysinfo)))
+ is_win2025 = xxx
+ endif
+else ifneq (,$(findstring sh.exe,$(SHELL)))
+ is_win = xxx
+ is_cmd = xxx
+endif
+isnot_done_ccdefs = xxx
diff --git a/third_party/mcpc/src/.indent.pro b/third_party/mcpc/src/.indent.pro
new file mode 100644
index 0000000..f7bb493
--- /dev/null
+++ b/third_party/mcpc/src/.indent.pro
@@ -0,0 +1 @@
+../.indent.pro \ No newline at end of file
diff --git a/third_party/mcpc/src/CMakeLists.txt b/third_party/mcpc/src/CMakeLists.txt
new file mode 100644
index 0000000..6933543
--- /dev/null
+++ b/third_party/mcpc/src/CMakeLists.txt
@@ -0,0 +1,32 @@
+set (libmcpc_SOURCES)
+
+# add_compile_options(-std=c23)
+# add_compile_options(-Dis_unix)
+
+add_compile_options (/Dis_win)
+add_compile_options (/Dis_wincl)
+add_compile_options (/nologo)
+add_compile_options (/std:c17)
+add_compile_options (/DMCPC_PTCH_C23KW1)
+add_compile_options (/DMCPC_PTCH_C23CKD1)
+add_compile_options (/DMCPC_PTCH_C23UCHAR1)
+
+
+list (APPEND libmcpc_SOURCES alloc.c)
+list (APPEND libmcpc_SOURCES log.c)
+list (APPEND libmcpc_SOURCES errcode.c)
+list (APPEND libmcpc_SOURCES tool.c)
+list (APPEND libmcpc_SOURCES rsc.c)
+list (APPEND libmcpc_SOURCES server.c)
+list (APPEND libmcpc_SOURCES retbuf.c)
+list (APPEND libmcpc_SOURCES ucbr.c)
+list (APPEND libmcpc_SOURCES serlz.c)
+list (APPEND libmcpc_SOURCES mjson.c)
+
+add_library (mcpc STATIC ${libmcpc_SOURCES})
+
+target_include_directories (
+ mcpc
+ PRIVATE ${PROJECT_SOURCE_DIR}
+)
+target_link_libraries (mcpc ws2_32)
diff --git a/third_party/mcpc/src/Makefile b/third_party/mcpc/src/Makefile
new file mode 100644
index 0000000..b9c5c4c
--- /dev/null
+++ b/third_party/mcpc/src/Makefile
@@ -0,0 +1,94 @@
+include ../mk/step0.mk
+
+ifneq (,$(is_linux))
+ include ../mk/ccdefs-linux.mk
+endif
+ifneq (,$(is_cygwin))
+ include ../mk/ccdefs-cygwin.mk
+endif
+ifneq (,$(is_mingw))
+ include ../mk/ccdefs-mingw.mk
+endif
+ifneq (,$(is_macos))
+ include ../mk/ccdefs-macos.mk
+endif
+ifneq (,$(is_win))
+ include ../mk/ccdefs-win.mk
+endif
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+include ../mk/dbgrel.mk
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+default: link
+
+clean:
+ $(if $(is_cmd) , del *.pdb *.ilk *.o *.out *.a *.so *.dSYM *.obj *.exe *.lib *.dll *.exp )
+ $(if $(is_sh) , rm -rf *.pdb *.ilk *.o *.out *.a *.so *.dSYM *.obj *.exe *.lib *.dll *.exp )
+
+comp: libmcpc_COMP
+
+link: libmcpc_LINKS
+link: libmcpc_LINKD
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# libmcpc
+
+libmcpc_srcs =
+libmcpc_srcs += alloc.c
+libmcpc_srcs += log.c
+libmcpc_srcs += errcode.c
+libmcpc_srcs += anydata.c
+libmcpc_srcs += tool.c
+libmcpc_srcs += rsc.c
+libmcpc_srcs += prmpt.c
+libmcpc_srcs += server.c
+libmcpc_srcs += retbuf.c
+libmcpc_srcs += ucbr.c
+libmcpc_srcs += complt.c
+libmcpc_srcs += serlz.c
+libmcpc_srcs += mjson.c
+
+libmcpc_objs =
+ifneq (,$(is_gcclike))
+ libmcpc_objs = $(patsubst %.c,%.o,$(libmcpc_srcs))
+endif
+ifneq (,$(is_cllike))
+ libmcpc_objs = $(patsubst %.c,%.obj,$(libmcpc_srcs))
+endif
+
+libmcpc_cflags =
+ifeq (,$(is_win))
+ libmcpc_cflags += -fPIC
+endif
+libmcpc_clcflags =
+
+libmcpc_ldflags =
+ifneq (,$(is_unixar))
+ libmcpc_ldflags += ../src/libmcpc.a
+endif
+ifneq (,$(is_winlib))
+ libmcpc_ldflags += ws2_32.lib
+endif
+ifneq (,$(is_winld))
+ libmcpc_ldflags += -lws2_32
+endif
+
+libmcpc_arflags =
+libmcpc_arflags += rcs
+
+libmcpc_COMP:
+ $(if $(is_gcclike) , $(CC) $(CFLAGS) $(libmcpc_cflags) $(libmcpc_srcs) -c )
+ $(if $(is_cllike) , $(CC) $(CFLAGS) $(libmcpc_clcflags) $(libmcpc_srcs) /c )
+
+libmcpc_LINKS: libmcpc_COMP
+ $(if $(is_gcclike) , $(AR) $(libmcpc_arflags) libmcpc.a $(libmcpc_objs) )
+ $(if $(is_cllike) , lib /nologo $(libmcpc_objs) /out:mcpc.lib $(libmcpc_ldflags) )
+
+libmcpc_LINKD: libmcpc_COMP
+ $(if $(is_gcclike) , $(CC) $(LDFLAGS) -o libmcpc.so $(libmcpc_objs) -shared $(libmcpc_ldflags) )
+ $(if $(is_cllike) , cl /nologo $(libmcpc_objs) /link /dll /out:mcpc.dll $(LDFLAGS) $(libmcpc_ldflags) )
+
+
diff --git a/third_party/mcpc/src/alloc.c b/third_party/mcpc/src/alloc.c
new file mode 100644
index 0000000..4771274
--- /dev/null
+++ b/third_party/mcpc/src/alloc.c
@@ -0,0 +1,57 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include "alloc.h"
+#include <string.h>
+
+// #define MCPC_CALLOC 1
+
+void *
+mcpc_alloc (const size_t nbytes)
+{
+ void *p =
+#ifdef MCPC_CALLOC
+ calloc (nbytes, 1)
+#else
+ malloc (nbytes)
+#endif
+ ;
+ return p;
+}
+
+void *
+mcpc_realloc (void *ptr, const size_t size)
+{
+ void *ret = realloc (ptr, size);
+
+#ifdef MCPC_CALLOC
+ memset (ret, 0, size);
+#endif
+
+ return ret;
+}
+
+void *
+mcpc_calloc (const size_t nbytes)
+{
+ void *p = calloc (nbytes, 1);
+ return p;
+}
diff --git a/third_party/mcpc/src/alloc.h b/third_party/mcpc/src/alloc.h
new file mode 100644
index 0000000..1742350
--- /dev/null
+++ b/third_party/mcpc/src/alloc.h
@@ -0,0 +1,39 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h__alloc
+#define h__alloc
+
+#if is_win || is_mingw
+#include <malloc.h>
+#else
+#include <alloca.h>
+#endif
+
+#include <stdlib.h>
+
+void *mcpc_alloc (const size_t nbytes);
+
+void *mcpc_realloc (void* ptr, const size_t size);
+
+void *mcpc_calloc (const size_t size);
+
+#endif
diff --git a/third_party/mcpc/src/anydata.c b/third_party/mcpc/src/anydata.c
new file mode 100644
index 0000000..0c663cd
--- /dev/null
+++ b/third_party/mcpc/src/anydata.c
@@ -0,0 +1,817 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include "anydata.h"
+
+#include "ext_string.h"
+
+#include <mcpc/errcode.h>
+#include "alloc.h"
+#include "errcode.h"
+
+const mcpc_anydata_t *mcpc_anypool_getbymeta1 (const mcpc_anypool_t * anypool, const char8_t * meta1, size_t meta1_len);
+const mcpc_anydata_t *mcpc_anypool_getbymeta1nt (const mcpc_anypool_t * anypool, const char8_t * meta1nt);
+
+constexpr static size_t mcpc_anypool_gamt = 8;
+
+/* = = = = = = = = = = = = = = = = anydata = = = = = = = = = = = = = = = = */
+
+mcpc_anydata_t *
+mcpc_anydata_new ()
+{
+ mcpc_anydata_t *hp_data = mcpc_calloc (sizeof (mcpc_anydata_t));
+ return hp_data;
+}
+
+mcpc_anydata_t *
+mcpc_anydata_new_typ (mcpc_anytype_t typ)
+{
+ mcpc_anydata_t *hp_data = mcpc_calloc (sizeof (mcpc_anydata_t));
+ hp_data->data_typ = typ;
+ return hp_data;
+}
+
+mcpc_anydata_t *
+mcpc_anydata_new_meta1_meta2 (const char8_t *meta1, const size_t meta1_len, const char8_t *meta2, const size_t meta2_len)
+{
+ mcpc_assert (meta1_len <= (size_t) UINT8_MAX, MCPC_EC_ANYDATA_LONGMETA1);
+ mcpc_assert (meta2_len <= (size_t) UINT16_MAX, MCPC_EC_ANYDATA_LONGMETA2);
+
+ mcpc_anydata_t *hp_data = mcpc_calloc (sizeof (mcpc_anydata_t));
+
+ void *hp_meta1 = mcpc_alloc (meta1_len);
+ memcpy (hp_meta1, meta1, meta1_len);
+ hp_data->meta1 = hp_meta1;
+ hp_data->meta1_len = (u8_t) meta1_len;
+
+ if (meta2_len > 0)
+ {
+ void *hp_meta2 = mcpc_alloc (meta2_len);
+ memcpy (hp_meta2, meta2, meta2_len);
+ hp_data->meta2 = hp_meta2;
+ hp_data->meta2_len = (u8_t) meta2_len;
+ }
+
+ return hp_data;
+}
+
+mcpc_anydata_t *
+mcpc_anydata_new_meta1nt_meta2nt (const char8_t *meta1nt, const char8_t *meta2nt)
+{
+ return mcpc_anydata_new_meta1_meta2 (meta1nt, u8strlen (meta1nt), meta2nt, u8strlen (meta2nt));
+}
+
+mcpc_anydata_t *
+mcpc_anydata_new_typ_meta1 (mcpc_anytype_t typ, const char8_t *meta1, const size_t meta1_len)
+{
+ mcpc_anydata_t *hp_data = mcpc_anydata_new_meta1_meta2 (meta1, meta1_len, nullptr, 0);
+ hp_data->data_typ = typ;
+ return hp_data;
+}
+
+mcpc_anydata_t *
+mcpc_anydata_new_typ_meta1_meta2 (mcpc_anytype_t typ, const char8_t *meta1, const size_t meta1_len, const char8_t *meta2,
+ const size_t meta2_len)
+{
+ mcpc_anydata_t *hp_data = mcpc_anydata_new_meta1_meta2 (meta1, meta1_len, meta2, meta2_len);
+ hp_data->data_typ = typ;
+ return hp_data;
+}
+
+mcpc_anydata_t *
+mcpc_anydata_new_typ_meta1nt_meta2nt (mcpc_anytype_t typ, const char8_t *meta1nt, const char8_t *meta2nt)
+{
+ return mcpc_anydata_new_typ_meta1_meta2 (typ, meta1nt, u8strlen (meta1nt), meta2nt, u8strlen (meta2nt));
+}
+
+void
+mcpc_anydata_mbfree (mcpc_anydata_t *data)
+{
+ bug_if (data == nullptr);
+
+ if (data->meta1 != nullptr)
+ {
+ free ((void *) data->meta1);
+ data->meta1_len = 0;
+ }
+
+ if (data->meta2 != nullptr)
+ {
+ free ((void *) data->meta2);
+ data->meta2_len = 0;
+ }
+
+ if (data->chd_pool != nullptr)
+ {
+ mcpc_anypool_free (data->chd_pool);
+ }
+
+ if (mcpc_anytype_is_arrlike (data->data_typ))
+ {
+ free ((void *) data->data_arrlk);
+ data->data_len = 0;
+ }
+}
+
+void
+mcpc_anydata_free (mcpc_anydata_t *data)
+{
+ mcpc_anydata_mbfree (data);
+ free (data);
+}
+
+void
+mcpc_anydata_copy (mcpc_anydata_t *dest_data, const mcpc_anydata_t *src_data)
+{
+ bug_if (dest_data == nullptr);
+ bug_if (src_data == nullptr);
+
+ // make sure inited
+ memset (dest_data, 0, sizeof (mcpc_anydata_t));
+
+ if (src_data->meta1_len > 0 && src_data->meta1 != nullptr)
+ {
+ void *hp_meta1 = mcpc_alloc (src_data->meta1_len);
+ memcpy (hp_meta1, src_data->meta1, src_data->meta1_len);
+ dest_data->meta1 = hp_meta1;
+ dest_data->meta1_len = src_data->meta1_len;
+ }
+
+ if (src_data->meta2_len > 0 && src_data->meta2 != nullptr)
+ {
+ void *hp_meta2 = mcpc_alloc (src_data->meta2_len);
+ memcpy (hp_meta2, src_data->meta2, src_data->meta2_len);
+ dest_data->meta2 = hp_meta2;
+ dest_data->meta2_len = src_data->meta2_len;
+ }
+
+ if (false);
+ else if (mcpc_anytype_is_arrlike (src_data->data_typ) && src_data->data_len > 0)
+ {
+ dest_data->data_arrlk = mcpc_alloc (src_data->data_len);
+ memcpy (dest_data->data_arrlk, src_data->data_arrlk, src_data->data_len);
+ }
+ else if (mcpc_anytype_is_intlike (src_data->data_typ))
+ dest_data->data_intlk = src_data->data_intlk;
+
+ if (src_data->chd_pool != nullptr)
+ {
+ dest_data->chd_pool = mcpc_anypool_new ();
+ mcpc_anypool_copy (dest_data->chd_pool, src_data->chd_pool);
+ }
+
+ dest_data->data_typ = src_data->data_typ;
+ dest_data->data_len = src_data->data_len;
+ dest_data->flags = src_data->flags;
+ dest_data->data_mime = src_data->data_mime;
+
+}
+
+void
+mcpc_anydata_add_child (mcpc_anydata_t *data, mcpc_anydata_t *child)
+{
+ bug_if_nullptr (data);
+ bug_if_nullptr ((void *) child);
+
+ if (data->chd_pool == nullptr)
+ {
+ data->chd_pool = mcpc_anypool_new ();
+ }
+ mcpc_anypool_addfre (data->chd_pool, child);
+}
+
+void
+mcpc_anydata_addcpy_child (mcpc_anydata_t *data, const mcpc_anydata_t *child)
+{
+ bug_if_nullptr (data);
+ bug_if_nullptr ((void *) child);
+
+ if (data->chd_pool == nullptr)
+ {
+ data->chd_pool = mcpc_anypool_new ();
+ }
+ mcpc_anypool_addcpy (data->chd_pool, child);
+}
+
+const mcpc_anydata_t *
+mcpc_anydata_get_child (const mcpc_anydata_t *anydata, size_t idx)
+{
+ bug_if (anydata == nullptr);
+ return mcpc_anypool_get (anydata->chd_pool, idx);
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_meta1 (mcpc_anydata_t *anydata, const char8_t *meta1, size_t meta1_len)
+{
+ bug_if_nullptr (anydata);
+ bug_if_nullptr ((void *) meta1);
+
+ if (meta1_len == 0)
+ {
+ free ((void *) anydata->meta1);
+ anydata->meta1 = nullptr;
+ anydata->meta1_len = 0;
+ return MCPC_EC_0;
+ }
+
+ if (meta1_len > (size_t) UINT8_MAX)
+ {
+ return MCPC_EC_ANYDATA_LONGMETA1;
+ }
+
+ anydata->meta1 = mcpc_realloc ((void *) anydata->meta1, meta1_len);
+ memcpy ((void *) anydata->meta1, meta1, meta1_len);
+ anydata->meta1_len = (u8_t) meta1_len;
+
+ return MCPC_EC_0;
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_meta1nt (mcpc_anydata_t *anydata, const char8_t *meta1)
+{
+ size_t meta1_len = u8strlen (meta1);
+ return mcpc_anydata_set_meta1 (anydata, meta1, meta1_len);
+}
+
+mcpc_errcode_t
+mcpc_anydata_release_data (mcpc_anydata_t *anydata)
+{
+ if (!mcpc_anytype_is_arrlike (anydata->data_typ))
+ return MCPC_EC_0;
+
+ if (anydata->data_len == 0)
+ return MCPC_EC_0;
+
+ free (anydata->data_arrlk);
+ anydata->data_len = 0;
+
+ return MCPC_EC_0;
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_intlk (mcpc_anydata_t *anydata, mcpc_anytype_t typ, const void *val)
+{
+ bug_if_nullptr (anydata);
+
+ if (mcpc_anytype_is_arrlike (anydata->data_typ))
+ mcpc_anydata_release_data (anydata);
+
+ if (false);
+ else if (typ == MCPC_I8 || typ == MCPC_U8)
+ anydata->data_intlk = (uintmax_t) (*((u8_t *) val));
+ else if (typ == MCPC_I16 || typ == MCPC_U16)
+ anydata->data_intlk = (uintmax_t) (*((u16_t *) val));
+ else if (typ == MCPC_I32 || typ == MCPC_U32)
+ anydata->data_intlk = (uintmax_t) (*((u32_t *) val));
+ else if (typ == MCPC_I64 || typ == MCPC_U64)
+ anydata->data_intlk = (uintmax_t) (*((u64_t *) val));
+ else if (typ == MCPC_FUNC)
+ anydata->data_intlk = (uintmax_t) (uintptr_t) (val);
+ else
+ return MCPC_EC_BUG;
+
+ anydata->data_typ = typ;
+ anydata->data_len = 1;
+
+ return MCPC_EC_0;
+}
+
+mcpc_errcode_t
+mcpc_anydata_get_intlk (const mcpc_anydata_t *anydata, mcpc_anytype_t typ, void *ret)
+{
+ bug_if_nullptr (anydata);
+ bug_if_nullptr (ret);
+
+ if (anydata->data_typ != typ)
+ return MCPC_EC_ANYDATA_NOTFOUND;
+
+ if (false);
+ else if (typ == MCPC_I8)
+ {
+ i8_t *ret_typed = ret;
+ *ret_typed = (i8_t) anydata->data_intlk;
+ }
+ else if (typ == MCPC_U8)
+ {
+ u8_t *ret_typed = ret;
+ *ret_typed = (u8_t) anydata->data_intlk;
+ }
+ else if (typ == MCPC_I16)
+ {
+ i16_t *ret_typed = ret;
+ *ret_typed = (i16_t) anydata->data_intlk;
+ }
+ else if (typ == MCPC_U16)
+ {
+ u16_t *ret_typed = ret;
+ *ret_typed = (u16_t) anydata->data_intlk;
+ }
+ else if (typ == MCPC_I32)
+ {
+ i32_t *ret_typed = ret;
+ *ret_typed = (i32_t) anydata->data_intlk;
+ }
+ else if (typ == MCPC_U32)
+ {
+ u32_t *ret_typed = ret;
+ *ret_typed = (u32_t) anydata->data_intlk;
+ }
+ else if (typ == MCPC_I64)
+ {
+ i64_t *ret_typed = ret;
+ *ret_typed = (i64_t) anydata->data_intlk;
+ }
+ else if (typ == MCPC_U64)
+ {
+ u64_t *ret_typed = ret;
+ *ret_typed = (u64_t) anydata->data_intlk;
+ }
+ else if (typ == MCPC_FUNC)
+ {
+ uintptr_t *ret_typed = ret;
+ *ret_typed = (uintptr_t) anydata->data_intlk;
+ }
+ else
+ return MCPC_EC_BUG;
+
+ return MCPC_EC_0;
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_arrlk (mcpc_anydata_t *anydata, mcpc_anytype_t typ, const void *arr, size_t arr_len)
+{
+ bug_if_nullptr (anydata);
+ bug_if_nullptr (arr);
+
+ size_t nbytes = 0;
+
+ if (false);
+ else if (typ == MCPC_I8S || typ == MCPC_U8S)
+ nbytes = arr_len * sizeof (u8_t);
+ else if (typ == MCPC_I16S || typ == MCPC_U16S)
+ nbytes = arr_len * sizeof (u16_t);
+ else if (typ == MCPC_I32S || typ == MCPC_U32S)
+ nbytes = arr_len * sizeof (u32_t);
+ else if (typ == MCPC_I64S || typ == MCPC_U64S)
+ nbytes = arr_len * sizeof (u64_t);
+ else if (typ == MCPC_U8STR)
+ nbytes = arr_len * sizeof (char8_t);
+ else
+ return MCPC_EC_BUG;
+
+ if (anydata->data_typ == typ)
+ {
+ anydata->data_arrlk = mcpc_realloc (anydata->data_arrlk, nbytes);
+ }
+ else
+ {
+ if (mcpc_anytype_is_arrlike (anydata->data_typ))
+ mcpc_anydata_release_data (anydata);
+
+ anydata->data_typ = typ;
+ anydata->data_arrlk = mcpc_alloc (nbytes);
+ }
+ memcpy (anydata->data_arrlk, arr, nbytes);
+ anydata->data_len = arr_len;
+
+ return MCPC_EC_0;
+}
+
+mcpc_errcode_t
+mcpc_anydata_get_arrlk (const mcpc_anydata_t *anydata, mcpc_anytype_t typ, void *ret, size_t ret_cap, size_t *ret_len)
+{
+ // NOTE ret_len can be nullptr
+
+ if (anydata->data_typ != typ)
+ return MCPC_EC_ANYDATA_NOTFOUND;
+ if (ret_cap == 0)
+ return MCPC_EC_ANYDATA_NOTFOUND;
+
+ size_t nb_el = 0;
+ if (false);
+ else if (typ == MCPC_I8S || typ == MCPC_U8S)
+ nb_el = sizeof (u8_t);
+ else if (typ == MCPC_I16S || typ == MCPC_U16S)
+ nb_el = sizeof (u16_t);
+ else if (typ == MCPC_I32S || typ == MCPC_U32S)
+ nb_el = sizeof (u32_t);
+ else if (typ == MCPC_I64S || typ == MCPC_U64S)
+ nb_el = sizeof (u64_t);
+ else if (typ == MCPC_U8STR)
+ nb_el = sizeof (char8_t);
+ else
+ return MCPC_EC_BUG;
+
+ size_t cplen = anydata->data_len;
+ if (cplen > ret_cap)
+ cplen = ret_cap;
+
+ memcpy (ret, anydata->data_arrlk, cplen * nb_el);
+ if (ret_len != nullptr)
+ *ret_len = cplen;
+
+ if (ret_cap > cplen)
+ {
+ char8_t *ret_typed = ret;
+ ret_typed[cplen] = 0;
+ return MCPC_EC_0;
+ }
+ else
+ {
+ return MCPC_EC_BUFCAP;
+ }
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_i8 (mcpc_anydata_t *anydata, i8_t val)
+{
+ return mcpc_anydata_set_intlk (anydata, MCPC_I8, &val);
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_u8 (mcpc_anydata_t *anydata, u8_t val)
+{
+ return mcpc_anydata_set_intlk (anydata, MCPC_U8, &val);
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_i16 (mcpc_anydata_t *anydata, i16_t val)
+{
+ return mcpc_anydata_set_intlk (anydata, MCPC_I16, &val);
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_u16 (mcpc_anydata_t *anydata, u16_t val)
+{
+ return mcpc_anydata_set_intlk (anydata, MCPC_U16, &val);
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_i32 (mcpc_anydata_t *anydata, i32_t val)
+{
+ return mcpc_anydata_set_intlk (anydata, MCPC_I32, &val);
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_u32 (mcpc_anydata_t *anydata, u32_t val)
+{
+ return mcpc_anydata_set_intlk (anydata, MCPC_U32, &val);
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_i64 (mcpc_anydata_t *anydata, i64_t val)
+{
+ return mcpc_anydata_set_intlk (anydata, MCPC_I64, &val);
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_u64 (mcpc_anydata_t *anydata, u64_t val)
+{
+ return mcpc_anydata_set_intlk (anydata, MCPC_U64, &val);
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_func (mcpc_anydata_t *anydata, void *val)
+{
+ return mcpc_anydata_set_intlk (anydata, MCPC_FUNC, val);
+}
+
+mcpc_errcode_t
+mcpc_anydata_get_i8 (const mcpc_anydata_t *anydata, i8_t *ret)
+{
+ return mcpc_anydata_get_intlk (anydata, MCPC_I8, ret);
+}
+
+mcpc_errcode_t
+mcpc_anydata_get_u8 (const mcpc_anydata_t *anydata, u8_t *ret)
+{
+ return mcpc_anydata_get_intlk (anydata, MCPC_U8, ret);
+}
+
+mcpc_errcode_t
+mcpc_anydata_get_i16 (const mcpc_anydata_t *anydata, i16_t *ret)
+{
+ return mcpc_anydata_get_intlk (anydata, MCPC_I16, ret);
+}
+
+mcpc_errcode_t
+mcpc_anydata_get_u16 (const mcpc_anydata_t *anydata, u16_t *ret)
+{
+ return mcpc_anydata_get_intlk (anydata, MCPC_U16, ret);
+}
+
+mcpc_errcode_t
+mcpc_anydata_get_i32 (const mcpc_anydata_t *anydata, i32_t *ret)
+{
+ return mcpc_anydata_get_intlk (anydata, MCPC_I32, ret);
+}
+
+mcpc_errcode_t
+mcpc_anydata_get_u32 (const mcpc_anydata_t *anydata, u32_t *ret)
+{
+ return mcpc_anydata_get_intlk (anydata, MCPC_U32, ret);
+}
+
+mcpc_errcode_t
+mcpc_anydata_get_i64 (const mcpc_anydata_t *anydata, i64_t *ret)
+{
+ return mcpc_anydata_get_intlk (anydata, MCPC_I64, ret);
+}
+
+mcpc_errcode_t
+mcpc_anydata_get_u64 (const mcpc_anydata_t *anydata, u64_t *ret)
+{
+ return mcpc_anydata_get_intlk (anydata, MCPC_U64, ret);
+}
+
+mcpc_errcode_t
+mcpc_anydata_get_func (const mcpc_anydata_t *anydata, void *ret)
+{
+ return mcpc_anydata_get_intlk (anydata, MCPC_FUNC, ret);
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_i8s (mcpc_anydata_t *anydata, const i8_t *arr, size_t arr_len)
+{
+ return mcpc_anydata_set_arrlk (anydata, MCPC_I8S, arr, arr_len);
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_u8s (mcpc_anydata_t *anydata, const u8_t *arr, size_t arr_len)
+{
+ return mcpc_anydata_set_arrlk (anydata, MCPC_U8S, arr, arr_len);
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_i16s (mcpc_anydata_t *anydata, const i16_t *arr, size_t arr_len)
+{
+ return mcpc_anydata_set_arrlk (anydata, MCPC_I16S, arr, arr_len);
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_u16s (mcpc_anydata_t *anydata, const u16_t *arr, size_t arr_len)
+{
+ return mcpc_anydata_set_arrlk (anydata, MCPC_U16S, arr, arr_len);
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_i32s (mcpc_anydata_t *anydata, const i32_t *arr, size_t arr_len)
+{
+ return mcpc_anydata_set_arrlk (anydata, MCPC_I32S, arr, arr_len);
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_u32s (mcpc_anydata_t *anydata, const u32_t *arr, size_t arr_len)
+{
+ return mcpc_anydata_set_arrlk (anydata, MCPC_U32S, arr, arr_len);
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_i64s (mcpc_anydata_t *anydata, const i64_t *arr, size_t arr_len)
+{
+ return mcpc_anydata_set_arrlk (anydata, MCPC_I64S, arr, arr_len);
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_u64s (mcpc_anydata_t *anydata, const u64_t *arr, size_t arr_len)
+{
+ return mcpc_anydata_set_arrlk (anydata, MCPC_U64S, arr, arr_len);
+}
+
+mcpc_errcode_t
+mcpc_anydata_set_u8str (mcpc_anydata_t *anydata, const char8_t *arr, size_t arr_len)
+{
+ return mcpc_anydata_set_arrlk (anydata, MCPC_U8STR, arr, arr_len);
+}
+
+mcpc_errcode_t
+mcpc_anydata_get_i8s (const mcpc_anydata_t *anydata, i8_t *ret, size_t ret_cap, size_t *ret_len)
+{
+ return mcpc_anydata_get_arrlk (anydata, MCPC_I8S, ret, ret_cap, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_anydata_get_u8s (const mcpc_anydata_t *anydata, u8_t *ret, size_t ret_cap, size_t *ret_len)
+{
+ return mcpc_anydata_get_arrlk (anydata, MCPC_U8S, ret, ret_cap, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_anydata_get_i16s (const mcpc_anydata_t *anydata, i16_t *ret, size_t ret_cap, size_t *ret_len)
+{
+ return mcpc_anydata_get_arrlk (anydata, MCPC_I16S, ret, ret_cap, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_anydata_get_u16s (const mcpc_anydata_t *anydata, u16_t *ret, size_t ret_cap, size_t *ret_len)
+{
+ return mcpc_anydata_get_arrlk (anydata, MCPC_U16S, ret, ret_cap, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_anydata_get_i32s (const mcpc_anydata_t *anydata, i32_t *ret, size_t ret_cap, size_t *ret_len)
+{
+ return mcpc_anydata_get_arrlk (anydata, MCPC_I32S, ret, ret_cap, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_anydata_get_u32s (const mcpc_anydata_t *anydata, u32_t *ret, size_t ret_cap, size_t *ret_len)
+{
+ return mcpc_anydata_get_arrlk (anydata, MCPC_U32S, ret, ret_cap, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_anydata_get_i64s (const mcpc_anydata_t *anydata, i64_t *ret, size_t ret_cap, size_t *ret_len)
+{
+ return mcpc_anydata_get_arrlk (anydata, MCPC_I64S, ret, ret_cap, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_anydata_get_u64s (const mcpc_anydata_t *anydata, u64_t *ret, size_t ret_cap, size_t *ret_len)
+{
+ return mcpc_anydata_get_arrlk (anydata, MCPC_U64S, ret, ret_cap, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_anydata_get_u8str (const mcpc_anydata_t *anydata, char8_t *ret, size_t ret_cap, size_t *ret_len)
+{
+ return mcpc_anydata_get_arrlk (anydata, MCPC_U8STR, ret, ret_cap, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_anydata_getref_u8str (const mcpc_anydata_t *anydata, const char8_t **ret, size_t *ret_len)
+{
+ bug_if_nullptr (anydata);
+ bug_if_nullptr (ret);
+ bug_if_nullptr (ret_len);
+
+ *ret = (const char8_t *) anydata->data_arrlk;
+ *ret_len = anydata->data_len;
+
+ return MCPC_EC_0;
+}
+
+mcpc_errcode_t
+mcpc_anydata_getref_meta1 (const mcpc_anydata_t *anydata, const char8_t **ret, size_t *ret_len)
+{
+ bug_if_nullptr (anydata);
+ bug_if_nullptr (ret);
+ bug_if_nullptr (ret_len);
+
+ *ret = anydata->meta1;
+ *ret_len = anydata->meta1_len;
+
+ return MCPC_EC_0;
+}
+
+mcpc_errcode_t
+mcpc_anydata_getref_meta2 (const mcpc_anydata_t *anydata, const char8_t **ret, size_t *ret_len)
+{
+ bug_if_nullptr (anydata);
+ bug_if_nullptr (ret);
+ bug_if_nullptr (ret_len);
+
+ *ret = anydata->meta2;
+ *ret_len = anydata->meta2_len;
+
+ return MCPC_EC_0;
+}
+
+/* = = = = = = = = = = = = = = = = anypool = = = = = = = = = = = = = = = = */
+
+mcpc_anypool_t *
+mcpc_anypool_new ()
+{
+ mcpc_anypool_t *hp_anypool = mcpc_calloc (sizeof (mcpc_anypool_t));
+ return hp_anypool;
+}
+
+void
+mcpc_anypool_free (mcpc_anypool_t *anypool)
+{
+ bug_if (anypool == nullptr);
+ for (size_t i = 0; i < anypool->len; i++)
+ {
+ mcpc_anydata_t *cur_data = (mcpc_anydata_t *) anypool->head + i;
+ mcpc_anydata_mbfree (cur_data);
+ }
+ if (anypool->cap > 0 && anypool->head != nullptr)
+ {
+ free ((void *) anypool->head);
+ }
+ free (anypool);
+}
+
+
+void
+mcpc_anypool_set_cap (mcpc_anypool_t *pool, size_t newcap)
+{
+ size_t newsz = newcap * sizeof (mcpc_anydata_t);
+ pool->head = mcpc_realloc ((void *) pool->head, newsz); // TODO use wrapper
+ pool->cap = newcap;
+}
+
+
+void
+mcpc_anypool_copy (mcpc_anypool_t *dest_pool, const mcpc_anypool_t *src_pool)
+{
+ bug_if (dest_pool == nullptr);
+ bug_if (src_pool == nullptr);
+
+ // make sure inited. this also means dest's content is insignificant
+ memset (dest_pool, 0, sizeof (mcpc_anypool_t));
+
+ mcpc_anypool_set_cap (dest_pool, src_pool->cap);
+
+ for (size_t i = 0; i < src_pool->len; i++) // TODO ckdadd
+ mcpc_anydata_copy ((mcpc_anydata_t *) dest_pool->head + i, src_pool->head + i);
+
+ dest_pool->len = src_pool->len;
+}
+
+void
+mcpc_anypool_grow (mcpc_anypool_t *pool)
+{
+ size_t newcap = pool->cap + mcpc_anypool_gamt;
+ mcpc_anypool_set_cap (pool, newcap);
+}
+
+void
+mcpc_anypool_addcpy (mcpc_anypool_t *anypool, const mcpc_anydata_t *tmp_data)
+{
+ bug_if (anypool == nullptr);
+ bug_if (tmp_data == nullptr);
+
+ if (anypool->len + 1 > anypool->cap)
+ {
+ mcpc_anypool_grow (anypool);
+ }
+ bug_if (anypool->len + 1 > anypool->cap);
+
+ mcpc_anydata_t *newdata = (mcpc_anydata_t *) (anypool->head + anypool->len);
+ mcpc_anydata_copy (newdata, tmp_data);
+ anypool->len += 1;
+}
+
+void
+mcpc_anypool_addfre (mcpc_anypool_t *anypool, mcpc_anydata_t *tmp_data)
+{
+ mcpc_anypool_addcpy (anypool, tmp_data);
+ mcpc_anydata_free (tmp_data);
+}
+
+const mcpc_anydata_t *
+mcpc_anypool_get (const mcpc_anypool_t *anypool, size_t idx)
+{
+ bug_if (anypool == nullptr);
+ if (idx >= anypool->len)
+ return nullptr;
+ return anypool->head + idx;
+}
+
+const mcpc_anydata_t *
+mcpc_anypool_getbymeta1 (const mcpc_anypool_t *anypool, const char8_t *meta1, size_t meta1_len)
+{
+ bug_if (anypool == nullptr);
+
+ if (meta1 == nullptr || meta1_len == 0)
+ return nullptr;
+
+ for (size_t i = 0; i < anypool->len; i++) // TODO: ckdadd
+ {
+ const mcpc_anydata_t *cur_data = anypool->head + i;
+ if (cur_data->meta1_len > 0)
+ {
+ size_t cmpsz = min_size (meta1_len, (size_t) cur_data->meta1_len);
+ if (0 == u8strncmp (cur_data->meta1, meta1, cmpsz))
+ return cur_data;
+ }
+ }
+
+ return nullptr;
+}
+
+const mcpc_anydata_t *
+mcpc_anypool_getbymeta1nt (const mcpc_anypool_t *pool, const char8_t *meta1nt)
+{
+ size_t meta1nt_len = u8strlen (meta1nt);
+ return mcpc_anypool_getbymeta1 (pool, meta1nt, meta1nt_len);
+}
diff --git a/third_party/mcpc/src/anydata.h b/third_party/mcpc/src/anydata.h
new file mode 100644
index 0000000..1188e05
--- /dev/null
+++ b/third_party/mcpc/src/anydata.h
@@ -0,0 +1,225 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h_anydata
+#define h_anydata
+
+#include <stddef.h>
+
+#include "ext_stdint.h"
+
+#include <mcpc/_c23_keywords.h>
+#include <mcpc/_c23_uchar.h>
+
+#include <mcpc/anydata.h>
+#include <mcpc/errcode.h>
+
+// ---------------------------- anydata ----------------------------
+
+#ifdef MCPC_C23GIVUP_FIXENUM
+enum mcpc_anydata_flags
+#else
+enum mcpc_anydata_flags:u16_t
+#endif
+{
+ ANYDATA_FL_REQUIRED = 1 << 0,
+ ANYDATA_FL_TOOL = 1 << 1,
+ ANYDATA_FL_TOOLPROP = 1 << 2,
+ ANYDATA_FL_RSC = 1 << 3,
+ ANYDATA_FL_PRMPT = 1 << 4,
+ ANYDATA_FL_PRMPTARG = 1 << 5,
+ ANYDATA_FL_USRCBRES_TOOLCALL = 1 << 6,
+ ANYDATA_FL_USRCBRES_TOOLCALL_TXTCTN = 1 << 7,
+ ANYDATA_FL_USRCBRES_TOOLCALL_IMGCTN = 1 << 8,
+ ANYDATA_FL_USRCBRES_PRMPTCALL = 1 << 9,
+ ANYDATA_FL_USRCBRES_PRMPTCALL_ASSIST = 1 << 10,
+ ANYDATA_FL_COMPLT = 1 << 11,
+ ANYDATA_FL_PRMPTARGHINT = 1 << 12,
+ ANYDATA_FL_PRESENT = 1 << 13,
+};
+typedef enum mcpc_anydata_flags mcpc_anydata_flags_t;
+
+struct mcpc_anydata
+{
+ u8_t meta1_len;
+ mcpc_mime_t data_mime;
+ mcpc_anytype_t data_typ;
+ mcpc_anydata_flags_t flags;
+ u16_t meta2_len;
+ size_t data_len;
+ const char8_t *meta1;
+ const char8_t *meta2;
+ mcpc_anypool_t *chd_pool;
+ union
+ {
+ uintmax_t data_intlk;
+ uintptr_t *data_arrlk;
+ };
+};
+
+mcpc_anydata_t *mcpc_anydata_new ();
+
+mcpc_anydata_t *mcpc_anydata_new_typ (mcpc_anytype_t typ);
+
+mcpc_anydata_t *mcpc_anydata_new_meta1_meta2 (const char8_t * meta1,
+ const size_t meta1_len, const char8_t * meta2, const size_t meta2_len);
+
+mcpc_anydata_t *mcpc_anydata_new_meta1nt_meta2nt (const char8_t * meta1nt, const char8_t * meta2nt);
+
+mcpc_anydata_t *mcpc_anydata_new_typ_meta1 (mcpc_anytype_t typ, const char8_t * meta1, const size_t meta1_len);
+
+mcpc_anydata_t *mcpc_anydata_new_typ_meta1_meta2 (mcpc_anytype_t typ,
+ const char8_t * meta1,
+ const size_t meta1_len, const char8_t * meta2,
+ const size_t meta2_len);
+
+mcpc_anydata_t *mcpc_anydata_new_typ_meta1nt_meta2nt (mcpc_anytype_t typ,
+ const char8_t * meta1nt, const char8_t * meta2nt);
+
+void mcpc_anydata_free (mcpc_anydata_t * data);
+
+void mcpc_anydata_copy (mcpc_anydata_t * dest_data, const mcpc_anydata_t * src_data);
+
+mcpc_errcode_t mcpc_anydata_set_meta1nt (mcpc_anydata_t * anydata, const char8_t * meta1);
+
+mcpc_errcode_t mcpc_anydata_set_i8 (mcpc_anydata_t * anydata, i8_t val);
+
+mcpc_errcode_t mcpc_anydata_set_u8 (mcpc_anydata_t * anydata, u8_t val);
+
+mcpc_errcode_t mcpc_anydata_set_i16 (mcpc_anydata_t * anydata, i16_t val);
+
+mcpc_errcode_t mcpc_anydata_set_u16 (mcpc_anydata_t * anydata, u16_t val);
+
+mcpc_errcode_t mcpc_anydata_set_i32 (mcpc_anydata_t * anydata, i32_t val);
+
+mcpc_errcode_t mcpc_anydata_set_u32 (mcpc_anydata_t * anydata, u32_t val);
+
+mcpc_errcode_t mcpc_anydata_set_i64 (mcpc_anydata_t * anydata, i64_t val);
+
+mcpc_errcode_t mcpc_anydata_set_u64 (mcpc_anydata_t * anydata, u64_t val);
+
+mcpc_errcode_t mcpc_anydata_set_func (mcpc_anydata_t * anydata, void *val);
+
+mcpc_errcode_t mcpc_anydata_get_i8 (const mcpc_anydata_t * anydata, i8_t * ret);
+
+mcpc_errcode_t mcpc_anydata_get_u8 (const mcpc_anydata_t * anydata, u8_t * ret);
+
+mcpc_errcode_t mcpc_anydata_get_i16 (const mcpc_anydata_t * anydata, i16_t * ret);
+
+mcpc_errcode_t mcpc_anydata_get_u16 (const mcpc_anydata_t * anydata, u16_t * ret);
+
+mcpc_errcode_t mcpc_anydata_get_i32 (const mcpc_anydata_t * anydata, i32_t * ret);
+
+mcpc_errcode_t mcpc_anydata_get_u32 (const mcpc_anydata_t * anydata, u32_t * ret);
+
+mcpc_errcode_t mcpc_anydata_get_i64 (const mcpc_anydata_t * anydata, i64_t * ret);
+
+mcpc_errcode_t mcpc_anydata_get_u64 (const mcpc_anydata_t * anydata, u64_t * ret);
+
+mcpc_errcode_t mcpc_anydata_get_func (const mcpc_anydata_t * anydata, void *ret);
+
+mcpc_errcode_t mcpc_anydata_set_i8s (mcpc_anydata_t * anydata, const i8_t * arr, size_t arr_len);
+
+mcpc_errcode_t mcpc_anydata_set_u8s (mcpc_anydata_t * anydata, const u8_t * arr, size_t arr_len);
+
+mcpc_errcode_t mcpc_anydata_set_i16s (mcpc_anydata_t * anydata, const i16_t * arr, size_t arr_len);
+
+mcpc_errcode_t mcpc_anydata_set_u16s (mcpc_anydata_t * anydata, const u16_t * arr, size_t arr_len);
+
+mcpc_errcode_t mcpc_anydata_set_i32s (mcpc_anydata_t * anydata, const i32_t * arr, size_t arr_len);
+
+mcpc_errcode_t mcpc_anydata_set_u32s (mcpc_anydata_t * anydata, const u32_t * arr, size_t arr_len);
+
+mcpc_errcode_t mcpc_anydata_set_i64s (mcpc_anydata_t * anydata, const i64_t * arr, size_t arr_len);
+
+mcpc_errcode_t mcpc_anydata_set_u64s (mcpc_anydata_t * anydata, const u64_t * arr, size_t arr_len);
+
+mcpc_errcode_t mcpc_anydata_set_u8str (mcpc_anydata_t * anydata, const char8_t * arr, size_t arr_len);
+
+mcpc_errcode_t mcpc_anydata_get_i8s (const mcpc_anydata_t * anydata, i8_t * ret, size_t ret_cap, size_t *ret_len);
+
+mcpc_errcode_t mcpc_anydata_get_u8s (const mcpc_anydata_t * anydata, u8_t * ret, size_t ret_cap, size_t *ret_len);
+
+mcpc_errcode_t mcpc_anydata_get_i16s (const mcpc_anydata_t * anydata, i16_t * ret, size_t ret_cap, size_t *ret_len);
+
+mcpc_errcode_t mcpc_anydata_get_u16s (const mcpc_anydata_t * anydata, u16_t * ret, size_t ret_cap, size_t *ret_len);
+
+mcpc_errcode_t mcpc_anydata_get_i32s (const mcpc_anydata_t * anydata, i32_t * ret, size_t ret_cap, size_t *ret_len);
+
+mcpc_errcode_t mcpc_anydata_get_u32s (const mcpc_anydata_t * anydata, u32_t * ret, size_t ret_cap, size_t *ret_len);
+
+mcpc_errcode_t mcpc_anydata_get_i64s (const mcpc_anydata_t * anydata, i64_t * ret, size_t ret_cap, size_t *ret_len);
+
+mcpc_errcode_t mcpc_anydata_get_u64s (const mcpc_anydata_t * anydata, u64_t * ret, size_t ret_cap, size_t *ret_len);
+
+mcpc_errcode_t mcpc_anydata_get_u8str (const mcpc_anydata_t * anydata, char8_t * ret, size_t ret_cap, size_t *ret_len);
+
+mcpc_errcode_t mcpc_anydata_getref_u8str (const mcpc_anydata_t * anydata, const char8_t ** ret, size_t *ret_len);
+
+mcpc_errcode_t mcpc_anydata_getref_meta1 (const mcpc_anydata_t * anydata, const char8_t ** ret, size_t *ret_len);
+
+mcpc_errcode_t mcpc_anydata_getref_meta2 (const mcpc_anydata_t * anydata, const char8_t ** ret, size_t *ret_len);
+
+const mcpc_anydata_t *mcpc_anydata_get_child (const mcpc_anydata_t * anydata, size_t idx);
+
+void mcpc_anydata_add_child (mcpc_anydata_t * data, mcpc_anydata_t * child);
+
+void mcpc_anydata_addcpy_child (mcpc_anydata_t * data, const mcpc_anydata_t * child);
+
+// ---------------------------- anypool ----------------------------
+
+#ifdef MCPC_C23GIVUP_FIXENUM
+enum mcpc_anypool_flags
+#else
+enum mcpc_anypool_flags:u16_t
+#endif
+{
+ ANYPOOL_FL_USRCBRES_TOOLCALL = 1 << 0,
+ ANYPOOL_FL_USRCBRES_TOOLCALL_ERROR = 1 << 1,
+ ANYPOOL_FL_USRCBRES_PRMPTCALL = 1 << 2,
+};
+typedef enum mcpc_anypool_flags mcpc_anypool_flags_t;
+
+struct mcpc_anypool
+{
+ mcpc_anypool_flags_t flags;
+ size_t cap;
+ size_t len;
+ const mcpc_anydata_t *head;
+};
+
+const mcpc_anydata_t *mcpc_anypool_get (const mcpc_anypool_t * anypool, size_t idx);
+
+const mcpc_anydata_t *mcpc_anypool_getbymeta1 (const mcpc_anypool_t * anypool, const char8_t * meta1, size_t meta1_len);
+
+const mcpc_anydata_t *mcpc_anypool_getbymeta1nt (const mcpc_anypool_t * pool, const char8_t * meta1nt);
+
+mcpc_anypool_t *mcpc_anypool_new ();
+
+void mcpc_anypool_free (mcpc_anypool_t * anypool);
+
+void mcpc_anypool_copy (mcpc_anypool_t * dest_pool, const mcpc_anypool_t * src_pool);
+
+void mcpc_anypool_addfre (mcpc_anypool_t * anypool, mcpc_anydata_t * tmp_data);
+
+void mcpc_anypool_addcpy (mcpc_anypool_t * anypool, const mcpc_anydata_t * tmp_data);
+
+#endif
diff --git a/third_party/mcpc/src/complt.c b/third_party/mcpc/src/complt.c
new file mode 100644
index 0000000..730d53b
--- /dev/null
+++ b/third_party/mcpc/src/complt.c
@@ -0,0 +1,162 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include "complt.h"
+
+#include "anydata.h"
+#include "ext_string.h"
+
+
+mcpc_complt_t *
+mcpc_complt_new ()
+{
+ mcpc_anydata_t *hp = mcpc_anydata_new ();
+
+ hp->flags |= ANYDATA_FL_COMPLT;
+
+ return hp;
+}
+
+mcpc_errcode_t
+mcpc_complt_add_compltcandi_u8str (mcpc_complt_t *complt, const char8_t *u8str, size_t u8str_len)
+{
+ bug_if_nullptr (complt);
+ bug_if_nullptr (u8str);
+ mcpc_anydata_t *chd = mcpc_anydata_new ();
+ bug_if_nz (mcpc_anydata_set_u8str (chd, u8str, u8str_len));
+ if (complt->chd_pool == nullptr)
+ complt->chd_pool = mcpc_anypool_new ();
+ (mcpc_anypool_addfre (complt->chd_pool, chd));
+ return MCPC_EC_0;
+}
+
+// provide completions for specific prompt arg, completing its hint, (currently hint is anydata with merely u8str data)
+mcpc_complt_t *
+mcpc_complt_new_prmptarg (const mcpc_prmpt_t *prmpt, const char8_t *argname, size_t argname_len, const char8_t *argval,
+ size_t argval_len)
+{
+ mcpc_complt_t *complt = mcpc_complt_new ();
+
+ const mcpc_prmptarg_t *prmptarg = mcpc_prmpt_getbyname_prmptarg (prmpt, argname, argname_len);
+ if (prmptarg == nullptr)
+ return complt;
+
+ bool match_any = argval_len == 0;
+
+ const mcpc_prmptarghint_t *cur_candi = nullptr;
+ while ((cur_candi = mcpc_prmptarg_get_next_prmptarghint (prmptarg)))
+ {
+ const char8_t *cur_val;
+ size_t cur_val_len;
+ bug_if_nz (mcpc_prmptarghint_getref_u8str (cur_candi, &cur_val, &cur_val_len));
+
+ if (match_any)
+ bug_if_nz (mcpc_complt_add_compltcandi_u8str (complt, cur_val, cur_val_len));
+ else
+ {
+ size_t cmpsz = min_size (cur_val_len, argval_len);
+ if (0 == u8strncmp (argval, cur_val, cmpsz))
+ {
+ bug_if_nz (mcpc_complt_add_compltcandi_u8str (complt, cur_val, cur_val_len));
+ }
+ }
+ }
+
+ return complt;
+}
+
+// provide completions for specific rsc, completing uri
+mcpc_complt_t *
+mcpc_complt_new_rsc (const mcpc_rscpool_t *rscpool, const char8_t *uriparts, size_t uriparts_len)
+{
+ mcpc_complt_t *complt = mcpc_complt_new ();
+
+ bool match_any = uriparts_len == 0;
+
+ const mcpc_rsc_t *cur_rsc = nullptr;
+ while ((cur_rsc = mcpc_rscpool_get_next (rscpool)))
+ {
+ const char8_t *cur_uri;
+ size_t cur_uri_len;
+ bug_if_nz (mcpc_rsc_getref_uri (cur_rsc, &cur_uri, &cur_uri_len));
+
+ if (match_any)
+ bug_if_nz (mcpc_complt_add_compltcandi_u8str (complt, cur_uri, cur_uri_len));
+ else if (uriparts_len <= cur_uri_len)
+ {
+ size_t cmpsz = uriparts_len;
+ if (0 == u8strncmp (cur_uri, uriparts, cmpsz))
+ bug_if_nz (mcpc_complt_add_compltcandi_u8str (complt, cur_uri, cur_uri_len));
+ }
+ }
+
+ return complt;
+}
+
+void
+mcpc_complt_free (mcpc_complt_t *complt)
+{
+ mcpc_anydata_free (complt);
+}
+
+mcpc_errcode_t
+mcpc_complt_get_compltcandi_len (const mcpc_complt_t *complt, size_t *ret)
+{
+ bug_if_nullptr (complt);
+ if (complt->chd_pool == nullptr)
+ *ret = 0;
+ else
+ *ret = complt->chd_pool->len;
+ return MCPC_EC_0;
+}
+
+const mcpc_compltcand_t *
+mcpc_complt_get_next_compltcand (const mcpc_complt_t *complt)
+{
+ bug_if_nullptr (complt);
+
+ static size_t idx = 0;
+
+ const mcpc_anypool_t *pool = complt->chd_pool;
+ if (pool == nullptr)
+ return nullptr;
+
+ const size_t pool_len = pool->len;
+ if (pool_len == 0)
+ return nullptr;
+
+ if (idx == pool_len) // TODO MT-SAFE
+ {
+ idx = 0;
+ return nullptr;
+ }
+
+ const mcpc_anydata_t *ret = pool->head + idx;
+ idx += 1;
+
+ return ret;
+}
+
+mcpc_errcode_t
+mcpc_compltcand_getref_u8str (const mcpc_compltcand_t *compltcand, const char8_t **ret, size_t *ret_len)
+{
+ return mcpc_anydata_getref_u8str (compltcand, ret, ret_len);
+}
diff --git a/third_party/mcpc/src/complt.h b/third_party/mcpc/src/complt.h
new file mode 100644
index 0000000..c11dc3f
--- /dev/null
+++ b/third_party/mcpc/src/complt.h
@@ -0,0 +1,60 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h_complt
+#define h_complt
+
+// not supposed to be used by lib users
+
+#include <mcpc/_c23_keywords.h>
+#include <mcpc/_c23_uchar.h>
+
+#include "ext_string.h"
+
+#include <mcpc/anydata.h>
+
+#include "errcode.h"
+
+#include "prmpt.h"
+#include "rsc.h"
+
+
+typedef mcpc_anydata_t mcpc_complt_t;
+typedef mcpc_anydata_t mcpc_compltcand_t;
+
+mcpc_complt_t *mcpc_complt_new_prmptarg (const mcpc_prmpt_t * prmpt, const char8_t * argname, size_t argname_len,
+ const char8_t * argval, size_t argval_len);
+
+mcpc_complt_t *mcpc_complt_new_rsc (const mcpc_rscpool_t * rscpool, const char8_t * uri, size_t uri_len);
+
+void mcpc_complt_free (mcpc_complt_t * complt);
+
+const mcpc_compltcand_t *mcpc_complt_get_next_compltcand (const mcpc_complt_t * complt);
+
+mcpc_errcode_t mcpc_complt_get_compltcandi_len (const mcpc_complt_t * complt, size_t *ret);
+
+
+mcpc_errcode_t
+mcpc_compltcand_getref_u8str (const mcpc_compltcand_t * compltcand, const char8_t ** ret, size_t *ret_len);
+
+
+
+#endif
diff --git a/third_party/mcpc/src/errcode.c b/third_party/mcpc/src/errcode.c
new file mode 100644
index 0000000..9fa5ddc
--- /dev/null
+++ b/third_party/mcpc/src/errcode.c
@@ -0,0 +1,39 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include "errcode.h"
+
+#include <stdlib.h>
+// #include <stdio.h>
+
+mcpc_errcode_t mcpc_ec = MCPC_EC_0;
+
+void mcpc_assert(const bool cond, const mcpc_errcode_t ec)
+{
+ if (cond)
+ {
+ return;
+ }
+ mcpc_ec = ec;
+ abort();
+ // fprintf(stderr,"!!!\n");
+ // exit((int)ec);
+}
diff --git a/third_party/mcpc/src/errcode.h b/third_party/mcpc/src/errcode.h
new file mode 100644
index 0000000..6cd1d88
--- /dev/null
+++ b/third_party/mcpc/src/errcode.h
@@ -0,0 +1,74 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h_errcode
+#define h_errcode
+
+
+#include <mcpc/errcode.h>
+#include <mcpc/_c23_keywords.h>
+
+
+
+// [[maybe_unused]] static mcpc_errcode_t mcpc_ec = MCPC_EC_CONNPOOL_NOTADD;
+// useless: after abort(), p mcpc_ec is always default value
+extern mcpc_errcode_t mcpc_ec;
+
+// [[noreturn]] ???
+void
+mcpc_assert(const bool cond, const mcpc_errcode_t ec);
+
+
+static inline void bug_if_nullptr(const void *ptr)
+{
+ if (ptr == nullptr)
+ {
+ mcpc_assert(false, MCPC_EC_BUG);
+ }
+}
+
+static inline void bug_if_zero(const unsigned long long ptr)
+{
+ if (ptr == 0)
+ {
+ mcpc_assert(false, MCPC_EC_BUG);
+ }
+}
+
+static inline void bug_if_nz(const unsigned long long ptr)
+{
+ if (ptr != 0)
+ {
+ mcpc_assert(false, MCPC_EC_BUG);
+ }
+}
+
+
+static inline void bug_if(bool f)
+{
+ if (f)
+ {
+ mcpc_assert(false, MCPC_EC_BUG);
+ }
+}
+
+
+#endif
diff --git a/third_party/mcpc/src/ext_stdint.h b/third_party/mcpc/src/ext_stdint.h
new file mode 100644
index 0000000..bc47e35
--- /dev/null
+++ b/third_party/mcpc/src/ext_stdint.h
@@ -0,0 +1,54 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h_ext_stdint
+#define h_ext_stdint
+
+#include <stdint.h>
+#include <stddef.h>
+
+typedef int8_t i8_t;
+typedef uint8_t u8_t;
+typedef int16_t i16_t;
+typedef uint16_t u16_t;
+typedef int32_t i32_t;
+typedef uint32_t u32_t;
+typedef int64_t i64_t;
+typedef uint64_t u64_t;
+
+static inline i32_t
+min_i32 (i32_t a, i32_t b)
+{
+ if (a < b)
+ return a;
+ else
+ return b;
+}
+
+static inline size_t
+min_size (size_t a, size_t b)
+{
+ if (b < a)
+ return b;
+ return a;
+}
+
+#endif
diff --git a/third_party/mcpc/src/ext_string.h b/third_party/mcpc/src/ext_string.h
new file mode 100644
index 0000000..87cc198
--- /dev/null
+++ b/third_party/mcpc/src/ext_string.h
@@ -0,0 +1,75 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h__string
+#define h__string
+
+#include <string.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <mcpc/_c23_keywords.h>
+#include <mcpc/_c23_uchar.h>
+
+#define u8c1(a) u8##"" a
+#define u8c2(a, b) u8##"" a b
+#define u8c3(a, b, c) u8##"" a b c
+#define u8c4(a, b, c, d) u8##"" a b c d
+#define u8c5(a, b, c, d, e) u8##"" a b c d e
+#define u8c6(a, b, c, d, e, f) u8##"" a b c d e f
+#define u8c7(a, b, c, d, e, f, g) u8##"" a b c d e f g
+
+inline static bool
+u8streq(const char8_t* a, const char8_t* b)
+{
+ const char* const aa = (const char* const)a;
+ const char* const bb = (const char* const)b;
+ return strcmp(aa, bb) == 0;
+}
+
+inline static size_t
+u8strlen(const char8_t* u8s)
+{
+ const char* s = (const char*)u8s;
+ return strlen(s);
+}
+
+static inline int
+u8strcmp (const char8_t *s1, const char8_t *s2)
+{
+ return strcmp ((const char *) s1, (const char *) s2);
+}
+
+static inline int
+u8strncmp (const char8_t *s1, const char8_t *s2, size_t n)
+{
+ return strncmp ((const char *) s1, (const char *) s2, n);
+}
+
+static inline bool
+beg_with_alph(const char8_t* const u8s)
+{
+ const char* const s = (const char* const)u8s;
+ return s[0] == '/' || isalpha((int)s[0]);
+}
+
+#endif
diff --git a/third_party/mcpc/src/log.c b/third_party/mcpc/src/log.c
new file mode 100644
index 0000000..ebf9879
--- /dev/null
+++ b/third_party/mcpc/src/log.c
@@ -0,0 +1,46 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include "log.h"
+
+LogLevel glv = Debug;
+
+void
+lvlog (LogLevel lv, const char *meta, FILE *stream, const char *format, ...)
+{
+ if (lv > glv)
+ return;
+
+ char logind = 'E';
+ if (lv == Info)
+ logind = 'I';
+ else if (lv == Debug)
+ logind = 'D';
+
+ va_list args;
+ va_start (args, format);
+ fprintf (stream, "[%c] %s ", logind, meta);
+ vfprintf (stream, format, args);
+ fprintf (stream, "\n");
+ fflush (stream);
+ fclose (stream);
+ va_end (args);
+}
diff --git a/third_party/mcpc/src/log.h b/third_party/mcpc/src/log.h
new file mode 100644
index 0000000..ae9eb72
--- /dev/null
+++ b/third_party/mcpc/src/log.h
@@ -0,0 +1,60 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h_log
+#define h_log
+
+#include <stdarg.h>
+#include <stdio.h>
+
+typedef enum
+{
+ Error = 1,
+ Info,
+ Debug,
+} LogLevel;
+
+extern LogLevel glv; // TODO: thread-safety
+
+#ifdef MCPC_DBG
+#define tulog_d(...) \
+ do \
+ { \
+ char meta[100]; \
+ sprintf(meta, "%s,%d", __TU__, __LINE__); \
+ lvlog(Debug, meta, stderr __VA_OPT__(, ) __VA_ARGS__); \
+ } while (false)
+#define tuflog_d(...) \
+ do \
+ { \
+ FILE *logf = fopen("/tmp/mcpc.log", "a"); \
+ char meta[100]; \
+ sprintf(meta, "%s,%d", __TU__, __LINE__); \
+ lvlog(Debug, meta, logf __VA_OPT__(, ) __VA_ARGS__); \
+ } while (false)
+#else
+#define tulog_d(...) ;
+#define tuflog_d(...) ;
+#endif
+
+void lvlog(LogLevel lv, const char *meta, FILE *stream, const char *format, ...);
+
+#endif
diff --git a/third_party/mcpc/src/mcpc.pc b/third_party/mcpc/src/mcpc.pc
new file mode 100644
index 0000000..4dbd624
--- /dev/null
+++ b/third_party/mcpc/src/mcpc.pc
@@ -0,0 +1,10 @@
+prefix=/usr/local
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: mcpc
+Description: A library for Model Context Protocol(MCP)
+URL: https://github.com/micl2e2/mcpc
+Version: 0.1.0
+Libs: -L${libdir} -lmcpc
+Cflags: -I${includedir}
diff --git a/third_party/mcpc/src/mjson.c b/third_party/mcpc/src/mjson.c
new file mode 100644
index 0000000..66123da
--- /dev/null
+++ b/third_party/mcpc/src/mjson.c
@@ -0,0 +1,1372 @@
+// Copyright (c) 2018-2020 Cesanta Software Limited
+// All rights reserved
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+#if is_wincl
+#pragma warning(disable : 5040)
+#endif
+
+#include <float.h>
+#include <math.h>
+
+#include "mjson.h"
+
+#if is_win || is_mingw
+#include <malloc.h>
+#else
+#include <alloca.h>
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1700
+#define va_copy(x, y) (x) = (y)
+#define isinf(x) !_finite(x)
+#define isnan(x) _isnan(x)
+#endif
+
+static double
+mystrtod(const char* str, const char** end);
+
+static int
+mjson_esc(int c, int esc)
+{
+ const char *p, *esc1 = "\b\f\n\r\t\\\"", *esc2 = "bfnrt\\\"";
+ for (p = esc ? esc1 : esc2; *p != '\0'; p++) {
+ if (*p == c)
+ return esc ? esc2[p - esc1] : esc1[p - esc2];
+ }
+ return 0;
+}
+
+static int
+mjson_escape(int c)
+{
+ return mjson_esc(c, 1);
+}
+
+static int
+mjson_pass_string(const char* s, int len)
+{
+ int i;
+ for (i = 0; i < len; i++) {
+ if (s[i] == '\\' && i + 1 < len && mjson_escape(s[i + 1])) {
+ i++;
+ } else if (s[i] == '\0') {
+ return MJSON_ERROR_INVALID_INPUT;
+ } else if (s[i] == '"') {
+ return i;
+ }
+ }
+ return MJSON_ERROR_INVALID_INPUT;
+}
+
+int
+mjson(const char* s, int len, mjson_cb_t cb, void* ud)
+{
+ enum
+ {
+ S_VALUE,
+ S_KEY,
+ S_COLON,
+ S_COMMA_OR_EOO
+ } expecting = S_VALUE;
+ unsigned char nesting[MJSON_MAX_DEPTH];
+ int i, depth = 0;
+#define MJSONCALL(ev) \
+ if (cb != NULL && cb(ev, s, start, i - start + 1, ud)) \
+ return i + 1;
+
+// In the ascii table, the distance between `[` and `]` is 2.
+// Ditto for `{` and `}`. Hence +2 in the code below.
+#define MJSONEOO() \
+ do { \
+ if (c != nesting[depth - 1] + 2) \
+ return MJSON_ERROR_INVALID_INPUT; \
+ depth--; \
+ if (depth == 0) { \
+ MJSONCALL(tok); \
+ return i + 1; \
+ } \
+ } while (0)
+
+ for (i = 0; i < len; i++) {
+ int start = i;
+ unsigned char c = ((const unsigned char*)s)[i];
+ int tok = c;
+ if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
+ continue;
+ // printf("- %c [%.*s] %d %d\n", c, i, s, depth, expecting);
+ switch (expecting) {
+ case S_VALUE:
+ if (c == '{') {
+ if (depth >= (int)sizeof(nesting))
+ return MJSON_ERROR_TOO_DEEP;
+ nesting[depth++] = c;
+ expecting = S_KEY;
+ break;
+ } else if (c == '[') {
+ if (depth >= (int)sizeof(nesting))
+ return MJSON_ERROR_TOO_DEEP;
+ nesting[depth++] = c;
+ break;
+ } else if (c == ']' && depth > 0) { // Empty array
+ MJSONEOO();
+ } else if (c == 't' && i + 3 < len && memcmp(&s[i], "true", 4) == 0) {
+ i += 3;
+ tok = MJSON_TOK_TRUE;
+ } else if (c == 'n' && i + 3 < len && memcmp(&s[i], "null", 4) == 0) {
+ i += 3;
+ tok = MJSON_TOK_NULL;
+ } else if (c == 'f' && i + 4 < len && memcmp(&s[i], "false", 5) == 0) {
+ i += 4;
+ tok = MJSON_TOK_FALSE;
+ } else if (c == '-' || ((c >= '0' && c <= '9'))) {
+ const char* end = NULL;
+ mystrtod(&s[i], &end);
+ if (end != NULL)
+ i += (int)(end - &s[i] - 1);
+ tok = MJSON_TOK_NUMBER;
+ } else if (c == '"') {
+ int n = mjson_pass_string(&s[i + 1], len - i - 1);
+ if (n < 0)
+ return n;
+ i += n + 1;
+ tok = MJSON_TOK_STRING;
+ } else {
+ return MJSON_ERROR_INVALID_INPUT;
+ }
+ if (depth == 0) {
+ MJSONCALL(tok);
+ return i + 1;
+ }
+ expecting = S_COMMA_OR_EOO;
+ break;
+
+ case S_KEY:
+ if (c == '"') {
+ int n = mjson_pass_string(&s[i + 1], len - i - 1);
+ if (n < 0)
+ return n;
+ i += n + 1;
+ tok = MJSON_TOK_KEY;
+ expecting = S_COLON;
+ } else if (c == '}') { // Empty object
+ MJSONEOO();
+ expecting = S_COMMA_OR_EOO;
+ } else {
+ return MJSON_ERROR_INVALID_INPUT;
+ }
+ break;
+
+ case S_COLON:
+ if (c == ':') {
+ expecting = S_VALUE;
+ } else {
+ return MJSON_ERROR_INVALID_INPUT;
+ }
+ break;
+
+ case S_COMMA_OR_EOO:
+ if (depth <= 0)
+ return MJSON_ERROR_INVALID_INPUT;
+ if (c == ',') {
+ expecting = (nesting[depth - 1] == '{') ? S_KEY : S_VALUE;
+ } else if (c == ']' || c == '}') {
+ MJSONEOO();
+ } else {
+ return MJSON_ERROR_INVALID_INPUT;
+ }
+ break;
+ }
+ MJSONCALL(tok);
+ }
+ return MJSON_ERROR_INVALID_INPUT;
+}
+
+struct msjon_get_data
+{
+ const char* path; // Lookup json path
+ int pos; // Current path position
+ int d1; // Current depth of traversal
+ int d2; // Expected depth of traversal
+ int i1; // Index in an array
+ int i2; // Expected index in an array
+ int obj; // If the value is array/object, offset where it starts
+ const char** tokptr; // Destination
+ int* toklen; // Destination length
+ int tok; // Returned token
+};
+
+#include <stdio.h>
+
+static int
+plen1(const char* s)
+{
+ int i = 0, n = 0;
+ while (s[i] != '\0' && s[i] != '.' && s[i] != '[')
+ n++, i += s[i] == '\\' ? 2 : 1;
+ // printf("PLEN: s: [%s], [%.*s] => %d\n", s, i, s, n);
+ return n;
+}
+
+static int
+plen2(const char* s)
+{
+ int i = 0, n = 0;
+ while (s[i] != '\0' && s[i] != '.' && s[i] != '[')
+ n++, i += s[i] == '\\' ? 2 : 1;
+ // printf("PLEN: s: [%s], [%.*s] => %d\n", s, i, s, n);
+ return i;
+}
+
+static int
+kcmp(const char* a, const char* b, int n)
+{
+ int i = 0, j = 0, r = 0;
+ for (i = 0, j = 0; j < n; i++, j++) {
+ if (b[i] == '\\')
+ i++;
+ if ((r = a[j] - b[i]) != 0)
+ return r;
+ }
+ // printf("KCMP: a: [%.*s], b:[%.*s] ==> %d\n", n, a, i, b, r);
+ return r;
+}
+
+static int
+mjson_get_cb(int tok, const char* s, int off, int len, void* ud)
+{
+ struct msjon_get_data* d = (struct msjon_get_data*)ud;
+#if 0
+ printf("--> %2x %2d %2d %2d %2d\t %2d %2d\t'%s' '%s' '%s' '%s'\n", tok, d->d1,
+ d->d2, d->i1, d->i2, (int) off, (int) d->pos, s, d->path, s + off,
+ d->path + d->pos);
+#endif
+ if (d->tok != MJSON_TOK_INVALID)
+ return 1; // Found
+ if (tok == '{' || tok == '[') {
+ if (d->d1 < d->d2)
+ d->obj = -1;
+ if (d->d1 == d->d2)
+ d->obj = off;
+ if (d->d1 == d->d2 && tok == '[' && d->path[d->pos] == '[') {
+ d->i1 = 0;
+ d->i2 = (int)mystrtod(&d->path[d->pos + 1], NULL);
+ if (d->i1 == d->i2) {
+ while (d->path[d->pos] && d->path[d->pos] != ']')
+ d->pos++;
+ if (d->path[d->pos] == ']')
+ d->pos++;
+ d->d2++;
+ }
+ }
+ d->d1++;
+ } else if (tok == '}' || tok == ']') {
+ if (tok == ']' && d->d1 == d->d2)
+ d->i1 = 0;
+ d->d1--;
+ // printf("X %s %d %d %d %d %d\n", d->path + d->pos, d->d1, d->d2, d->i1,
+ // d->i2, d->obj);
+ if (!d->path[d->pos] && d->d1 == d->d2 && d->obj != -1) {
+ d->tok = tok - 2;
+ if (d->tokptr)
+ *d->tokptr = s + d->obj;
+ if (d->toklen)
+ *d->toklen = off - d->obj + 1;
+ return 1;
+ }
+ } else if (tok == ',' && d->d1 == d->d2 && d->pos &&
+ d->path[d->pos - 1] == ']') {
+ return 1; // Not found in the current elem array
+ } else if (tok == ',' && d->d1 == d->d2 + 1 && d->path[d->pos] == '[') {
+ // printf("GG '%s' '%s'\n", d->path, &d->path[d->pos]);
+ d->i1++;
+ if (d->i1 == d->i2) {
+ while (d->path[d->pos] && d->path[d->pos] != ']')
+ d->pos++;
+ if (d->path[d->pos] == ']')
+ d->pos++;
+ d->d2++;
+ }
+ } else if (tok == MJSON_TOK_KEY && d->d1 == d->d2 + 1 &&
+ d->path[d->pos] == '.' && s[off] == '"' &&
+ s[off + len - 1] == '"' &&
+ plen1(&d->path[d->pos + 1]) == len - 2 &&
+ kcmp(s + off + 1, &d->path[d->pos + 1], len - 2) == 0) {
+ d->d2++;
+ d->pos += plen2(&d->path[d->pos + 1]) + 1;
+ } else if (tok == MJSON_TOK_KEY && d->d1 == d->d2) {
+ return 1; // Exhausted path, not found
+ } else if (MJSON_TOK_IS_VALUE(tok)) {
+ // printf("T %d %d %d %d %d\n", tok, d->d1, d->d2, d->i1, d->i2);
+ if (d->d1 == d->d2 && d->i1 == d->i2 && !d->path[d->pos]) {
+ d->tok = tok;
+ if (d->tokptr)
+ *d->tokptr = s + off;
+ if (d->toklen)
+ *d->toklen = len;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int
+mjson_find(const char* s, int n, const char* jp, const char** tp, int* tl)
+{
+ struct msjon_get_data data = { jp, 1, 0, 0, 0,
+ 0, -1, tp, tl, MJSON_TOK_INVALID };
+ if (jp[0] != '$')
+ return MJSON_TOK_INVALID;
+ if (mjson(s, n, mjson_get_cb, &data) < 0)
+ return MJSON_TOK_INVALID;
+ return data.tok;
+}
+
+int32_t
+mjson_find8(const char8_t* buf,
+ int32_t len,
+ const char8_t* jp,
+ const char8_t** tp,
+ int32_t* tl)
+{
+ return mjson_find(
+ (const char*)buf, (int)len, (const char*)jp, (const char**)tp, (int*)tl);
+}
+
+int32_t
+mjson_find88(const char* buf,
+ int32_t len,
+ const char8_t* jp,
+ const char8_t** tp,
+ int32_t* tl)
+{
+ return mjson_find(
+ (const char*)buf, (int)len, (const char*)jp, (const char**)tp, (int*)tl);
+}
+
+int
+mjson_get_number(const char* s, int len, const char* path, double* v)
+{
+ const char* p;
+ int tok, n;
+ if ((tok = mjson_find(s, len, path, &p, &n)) == MJSON_TOK_NUMBER) {
+ if (v != NULL)
+ *v = mystrtod(p, NULL);
+ }
+ return tok == MJSON_TOK_NUMBER ? 1 : 0;
+}
+
+int
+mjson_get_bool(const char* s, int len, const char* path, int* v)
+{
+ int tok = mjson_find(s, len, path, NULL, NULL);
+ if (tok == MJSON_TOK_TRUE && v != NULL)
+ *v = 1;
+ if (tok == MJSON_TOK_FALSE && v != NULL)
+ *v = 0;
+ return tok == MJSON_TOK_TRUE || tok == MJSON_TOK_FALSE ? 1 : 0;
+}
+
+static unsigned char
+unhex(unsigned char c)
+{
+ return (c >= '0' && c <= '9') ? (unsigned char)(c - '0')
+ : (c >= 'A' && c <= 'F') ? (unsigned char)(c - '7')
+ : (unsigned char)(c - 'W');
+}
+
+static unsigned char
+mjson_unhex_nimble(const char* s)
+{
+ const unsigned char* u = (const unsigned char*)s;
+ return (unsigned char)(((unsigned char)(unhex(u[0]) << 4)) | unhex(u[1]));
+}
+
+// n is at most?
+static int
+mjson_unescape(const char* s, int len, char* to, int n)
+{
+ int i, j;
+ for (i = 0, j = 0; i < len && j < n; i++, j++) {
+ if (s[i] == '\\' && i + 5 < len && s[i + 1] == 'u') {
+ // \uXXXX escape. We could process a simple one-byte chars
+ // \u00xx from the ASCII range. More complex chars would require
+ // dragging in a UTF8 library, which is too much for us
+ if (s[i + 2] != '0' || s[i + 3] != '0')
+ return -1; // Too much, give up
+ ((unsigned char*)to)[j] = mjson_unhex_nimble(s + i + 4);
+ i += 5;
+ } else if (s[i] == '\\' && i + 1 < len) {
+ int c = mjson_esc(s[i + 1], 0);
+ if (c == 0)
+ return -1;
+ to[j] = (char)(unsigned char)c;
+ i++;
+ } else {
+ to[j] = s[i];
+ }
+ }
+ if (j >= n)
+ return -1;
+ if (n > 0)
+ to[j] = '\0';
+ return j;
+}
+
+int
+mjson_get_string(const char* s, int len, const char* path, char* to, int n)
+{
+ const char* p;
+ int sz;
+ if (mjson_find(s, len, path, &p, &sz) != MJSON_TOK_STRING)
+ return -1;
+ return mjson_unescape(p + 1, sz - 2, to, n);
+}
+
+int
+mjson_get_hex(const char* s, int len, const char* x, char* to, int n)
+{
+ const char* p;
+ int i, j, sz;
+ if (mjson_find(s, len, x, &p, &sz) != MJSON_TOK_STRING)
+ return -1;
+ for (i = j = 0; i < sz - 3 && j < n; i += 2, j++) {
+ ((unsigned char*)to)[j] = mjson_unhex_nimble(p + i + 1);
+ }
+ if (j < n)
+ to[j] = '\0';
+ return j;
+}
+
+#if MJSON_ENABLE_BASE64
+static unsigned char
+mjson_base64rev(int c)
+{
+ if (c >= 'A' && c <= 'Z') {
+ return (unsigned char)(c - 'A');
+ } else if (c >= 'a' && c <= 'z') {
+ return (unsigned char)(c + 26 - 'a');
+ } else if (c >= '0' && c <= '9') {
+ return (unsigned char)(c + 52 - '0');
+ } else if (c == '+') {
+ return 62;
+ } else if (c == '/') {
+ return 63;
+ } else {
+ return 64;
+ }
+}
+
+int
+mjson_base64_dec(const char* src, int n, char* dst, int dlen)
+{
+ const char* end = src + n;
+ int len = 0;
+ while (src + 3 < end && len < dlen) {
+ unsigned char a = mjson_base64rev(src[0]), b = mjson_base64rev(src[1]),
+ c = mjson_base64rev(src[2]), d = mjson_base64rev(src[3]);
+ dst[len++] = (char)(unsigned char)((a << 2) | (b >> 4));
+ if (src[2] != '=' && len < dlen) {
+ dst[len++] = (char)(unsigned char)((b << 4) | (c >> 2));
+ if (src[3] != '=' && len < dlen) {
+ dst[len++] = (char)(unsigned char)((c << 6) | d);
+ }
+ }
+ src += 4;
+ }
+ if (len < dlen)
+ dst[len] = '\0';
+ return len;
+}
+
+int
+mjson_get_base64(const char* s, int len, const char* path, char* to, int n)
+{
+ const char* p;
+ int sz;
+ if (mjson_find(s, len, path, &p, &sz) != MJSON_TOK_STRING)
+ return 0;
+ return mjson_base64_dec(p + 1, sz - 2, to, n);
+}
+#endif // MJSON_ENABLE_BASE64
+
+#if MJSON_ENABLE_NEXT
+struct nextdata
+{
+ int off, len, depth, t, vo, arrayindex;
+ int *koff, *klen, *voff, *vlen, *vtype;
+};
+
+static int
+next_cb(int tok, const char* s, int off, int len, void* ud)
+{
+ struct nextdata* d = (struct nextdata*)ud;
+ // int i;
+ switch (tok) {
+ case '{':
+ case '[':
+ if (d->depth == 0 && tok == '[')
+ d->arrayindex = 0;
+ if (d->depth == 1 && off > d->off) {
+ d->vo = off;
+ d->t = tok == '{' ? MJSON_TOK_OBJECT : MJSON_TOK_ARRAY;
+ if (d->voff)
+ *d->voff = off;
+ if (d->vtype)
+ *d->vtype = d->t;
+ }
+ d->depth++;
+ break;
+ case '}':
+ case ']':
+ d->depth--;
+ if (d->depth == 1 && d->vo) {
+ d->len = off + len;
+ if (d->vlen)
+ *d->vlen = d->len - d->vo;
+ if (d->arrayindex >= 0) {
+ if (d->koff)
+ *d->koff = d->arrayindex; // koff holds array index
+ if (d->klen)
+ *d->klen = 0; // klen holds 0
+ }
+ return 1;
+ }
+ if (d->depth == 1 && d->arrayindex >= 0)
+ d->arrayindex++;
+ break;
+ case ',':
+ case ':':
+ break;
+ case MJSON_TOK_KEY:
+ if (d->depth == 1 && d->off < off) {
+ if (d->koff)
+ *d->koff = off; // And report back to the user
+ if (d->klen)
+ *d->klen = len; // If we have to
+ }
+ break;
+ default:
+ if (d->depth != 1)
+ break;
+ // If we're iterating over the array
+ if (off > d->off) {
+ d->len = off + len;
+ if (d->vlen)
+ *d->vlen = len; // value length
+ if (d->voff)
+ *d->voff = off; // value offset
+ if (d->vtype)
+ *d->vtype = tok; // value type
+ if (d->arrayindex >= 0) {
+ if (d->koff)
+ *d->koff = d->arrayindex; // koff holds array index
+ if (d->klen)
+ *d->klen = 0; // klen holds 0
+ }
+ return 1;
+ }
+ if (d->arrayindex >= 0)
+ d->arrayindex++;
+ break;
+ }
+ (void)s;
+ return 0;
+}
+
+int
+mjson_next(const char* s,
+ int n,
+ int off,
+ int* koff,
+ int* klen,
+ int* voff,
+ int* vlen,
+ int* vtype)
+{
+ struct nextdata d = { off, 0, 0, 0, 0, -1, koff, klen, voff, vlen, vtype };
+ mjson(s, n, next_cb, &d);
+ return d.len;
+}
+#endif
+
+#if MJSON_ENABLE_PRINT
+int
+mjson_print_fixed_buf(const char* ptr, int len, void* fn_data)
+{
+ struct mjson_fixedbuf* fb = (struct mjson_fixedbuf*)fn_data;
+ int i, left = fb->size - fb->len;
+ /* int i, left = fb->size - 1 - fb->len; */
+ if (left < len)
+ len = left;
+ for (i = 0; i < len; i++)
+ fb->ptr[fb->len + i] = ptr[i];
+ fb->len += len;
+ /* fb->ptr[fb->len] = '\0'; */
+ return len;
+}
+
+// This function allocates memory in chunks of size MJSON_DYNBUF_CHUNK
+// to decrease memory fragmentation, when many calls are executed to
+// print e.g. a base64 string or a hex string.
+int
+mjson_print_dynamic_buf(const char* ptr, int len, void* fn_data)
+{
+ char *s, *buf = *(char**)fn_data;
+ size_t curlen = buf == NULL ? 0 : strlen(buf);
+ size_t new_size = curlen + (size_t)len + 1 + MJSON_DYNBUF_CHUNK;
+ new_size -= new_size % MJSON_DYNBUF_CHUNK;
+
+ if ((s = (char*)MJSON_REALLOC(buf, new_size)) == NULL) {
+ return 0;
+ } else {
+ memcpy(s + curlen, ptr, (size_t)len);
+ s[curlen + (size_t)len] = '\0';
+ *(char**)fn_data = s;
+ return len;
+ }
+}
+
+int
+mjson_snprintf(char* buf, size_t len, const char* fmt, ...)
+{
+ va_list ap;
+ struct mjson_fixedbuf fb = { buf, (int)len, 0 };
+ va_start(ap, fmt);
+ mjson_vprintf(mjson_print_fixed_buf, &fb, fmt, &ap);
+ va_end(ap);
+ return fb.len;
+}
+
+char*
+mjson_aprintf(const char* fmt, ...)
+{
+ va_list ap;
+ char* result = NULL;
+ va_start(ap, fmt);
+ mjson_vprintf(mjson_print_dynamic_buf, &result, fmt, &ap);
+ va_end(ap);
+ return result;
+}
+
+int
+mjson_print_null(const char* ptr, int len, void* userdata)
+{
+ (void)ptr;
+ (void)userdata;
+ return len;
+}
+
+int
+mjson_print_buf(mjson_print_fn_t fn, void* fnd, const char* buf, int len)
+{
+ return fn(buf, len, fnd);
+}
+
+int
+mjson_print_long(mjson_print_fn_t fn, void* fnd, long val, int is_signed)
+{
+ unsigned long v = (unsigned long)val, s = 0, n, i;
+ char buf[20], t;
+ if (is_signed && val < 0)
+ buf[s++] = '-', v = (unsigned long)(-val);
+ // This loop prints a number in reverse order. I guess this is because we
+ // write numbers from right to left: least significant digit comes last.
+ // Maybe because we use Arabic numbers, and Arabs write RTL?
+ for (n = 0; v > 0; v /= 10)
+ buf[s + n++] = "0123456789"[v % 10];
+ // Reverse a string
+ for (i = 0; i < n / 2; i++)
+ t = buf[s + i], buf[s + i] = buf[s + n - i - 1], buf[s + n - i - 1] = t;
+ if (val == 0)
+ buf[n++] = '0'; // Handle special case
+ return fn(buf, (int)(s + n), fnd);
+}
+
+int
+mjson_print_int(mjson_print_fn_t fn, void* fnd, int v, int s)
+{
+ return mjson_print_long(fn, fnd, s ? (long)v : (long)(unsigned)v, s);
+}
+
+static int
+addexp(char* buf, int e, int sign)
+{
+ int n = 0;
+ buf[n++] = 'e';
+ buf[n++] = (char)sign;
+ if (e > 400)
+ return 0;
+ if (e < 10)
+ buf[n++] = '0';
+ if (e >= 100)
+ buf[n++] = (char)(e / 100 + '0'), e -= 100 * (e / 100);
+ if (e >= 10)
+ buf[n++] = (char)(e / 10 + '0'), e -= 10 * (e / 10);
+ buf[n++] = (char)(e + '0');
+ return n;
+}
+
+int
+mjson_print_dbl(mjson_print_fn_t fn, void* fnd, double d, int width)
+{
+ char buf[40];
+ int i, s = 0, n = 0, e = 0;
+ double t, mul, saved;
+ if (d == 0.0)
+ return fn("0", 1, fnd);
+ if (isinf(d))
+ return fn(d > 0 ? "inf" : "-inf", d > 0 ? 3 : 4, fnd);
+ if (isnan(d))
+ return fn("nan", 3, fnd);
+ if (d < 0.0)
+ d = -d, buf[s++] = '-';
+
+ // Round
+ saved = d;
+ mul = 1.0;
+ while (d >= 10.0 && d / mul >= 10.0)
+ mul *= 10.0;
+ while (d <= 1.0 && d / mul <= 1.0)
+ mul /= 10.0;
+ for (i = 0, t = mul * 5; i < width; i++)
+ t /= 10.0;
+ d += t;
+ // Calculate exponent, and 'mul' for scientific representation
+ mul = 1.0;
+ while (d >= 10.0 && d / mul >= 10.0)
+ mul *= 10.0, e++;
+ while (d < 1.0 && d / mul < 1.0)
+ mul /= 10.0, e--;
+ // printf(" --> %g %d %g %g\n", saved, e, t, mul);
+
+ if (e >= width) {
+ struct mjson_fixedbuf fb = { buf + s, (int)sizeof(buf) - s, 0 };
+ n = mjson_print_dbl(mjson_print_fixed_buf, &fb, saved / mul, width);
+ // printf(" --> %.*g %d [%.*s]\n", 10, d / t, e, fb.len, fb.ptr);
+ n += addexp(buf + s + n, e, '+');
+ return fn(buf, s + n, fnd);
+ } else if (e <= -width) {
+ struct mjson_fixedbuf fb = { buf + s, (int)sizeof(buf) - s, 0 };
+ n = mjson_print_dbl(mjson_print_fixed_buf, &fb, saved / mul, width);
+ // printf(" --> %.*g %d [%.*s]\n", 10, d / mul, e, fb.len, fb.ptr);
+ n += addexp(buf + s + n, -e, '-');
+ return fn(buf, s + n, fnd);
+ } else {
+ for (i = 0, t = mul; t >= 1.0 && s + n < (int)sizeof(buf); i++) {
+ int ch = (int)(d / t);
+ if (n > 0 || ch > 0)
+ buf[s + n++] = (char)(ch + '0');
+ d -= ch * t;
+ t /= 10.0;
+ }
+ // printf(" --> [%g] -> %g %g (%d) [%.*s]\n", saved, d, t, n, s + n, buf);
+ if (n == 0)
+ buf[s++] = '0';
+ while (t >= 1.0 && n + s < (int)sizeof(buf))
+ buf[n++] = '0', t /= 10.0;
+ if (s + n < (int)sizeof(buf))
+ buf[n + s++] = '.';
+ // printf(" 1--> [%g] -> [%.*s]\n", saved, s + n, buf);
+ for (i = 0, t = 0.1; s + n < (int)sizeof(buf) && n < width; i++) {
+ int ch = (int)(d / t);
+ buf[s + n++] = (char)(ch + '0');
+ d -= ch * t;
+ t /= 10.0;
+ }
+ }
+ while (n > 0 && buf[s + n - 1] == '0')
+ n--; // Trim trailing zeros
+ if (n > 0 && buf[s + n - 1] == '.')
+ n--; // Trim trailing dot
+ buf[s + n] = '\0';
+ return fn(buf, s + n, fnd);
+}
+
+int
+mjson_print_str(mjson_print_fn_t fn, void* fnd, const char* s, int len)
+{
+ int i, n = fn("\"", 1, fnd);
+ for (i = 0; i < len; i++) {
+ char c = (char)(unsigned char)mjson_escape(s[i]);
+ if (c) {
+ n += fn("\\", 1, fnd);
+ n += fn(&c, 1, fnd);
+ } else {
+ n += fn(&s[i], 1, fnd);
+ }
+ }
+ return n + fn("\"", 1, fnd);
+}
+
+#if MJSON_ENABLE_BASE64
+int
+mjson_print_b64(mjson_print_fn_t fn, void* fnd, const unsigned char* s, int n)
+{
+ const char* t =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ int i, len = fn("\"", 1, fnd);
+ for (i = 0; i < n; i += 3) {
+ int a = s[i], b = i + 1 < n ? s[i + 1] : 0, c = i + 2 < n ? s[i + 2] : 0;
+ char buf[4] = { t[a >> 2], t[(a & 3) << 4 | (b >> 4)], '=', '=' };
+ if (i + 1 < n)
+ buf[2] = t[(b & 15) << 2 | (c >> 6)];
+ if (i + 2 < n)
+ buf[3] = t[c & 63];
+ len += fn(buf, sizeof(buf), fnd);
+ }
+ return len + fn("\"", 1, fnd);
+}
+#endif /* MJSON_ENABLE_BASE64 */
+
+int
+mjson_vprintf(mjson_print_fn_t fn, void* fnd, const char* fmt, va_list* ap)
+{
+ int i = 0, n = 0;
+ while (fmt[i] != '\0') {
+ if (fmt[i] == '%') {
+ char fc = fmt[++i];
+ int is_long = 0;
+ if (fc == 'l') {
+ is_long = 1;
+ fc = fmt[i + 1];
+ }
+ if (fc == 'Q') {
+ char* buf = va_arg(*ap, char*);
+ n +=
+ mjson_print_str(fn, fnd, buf ? buf : "", buf ? (int)strlen(buf) : 0);
+ } else if (strncmp(&fmt[i], ".*Q", 3) == 0) {
+ int len = va_arg(*ap, int);
+ char* buf = va_arg(*ap, char*);
+ n += mjson_print_str(fn, fnd, buf, len);
+ i += 2;
+ } else if (fc == 'd' || fc == 'u') {
+ int is_signed = (fc == 'd');
+ if (is_long) {
+ long val = va_arg(*ap, long);
+ n += mjson_print_long(fn, fnd, val, is_signed);
+ i++;
+ } else {
+ int val = va_arg(*ap, int);
+ n += mjson_print_int(fn, fnd, val, is_signed);
+ }
+ } else if (fc == 'B') {
+ const char* s = va_arg(*ap, int) ? "true" : "false";
+ n += mjson_print_buf(fn, fnd, s, (int)strlen(s));
+ } else if (fc == 's') {
+ char* buf = va_arg(*ap, char*);
+ n += mjson_print_buf(fn, fnd, buf, (int)strlen(buf));
+ } else if (strncmp(&fmt[i], ".*s", 3) == 0) {
+ int len = va_arg(*ap, int);
+ char* buf = va_arg(*ap, char*);
+ n += mjson_print_buf(fn, fnd, buf, len);
+ i += 2;
+ } else if (fc == 'g') {
+ n += mjson_print_dbl(fn, fnd, va_arg(*ap, double), 6);
+ } else if (strncmp(&fmt[i], ".*g", 3) == 0) {
+ int width = va_arg(*ap, int);
+ n += mjson_print_dbl(fn, fnd, va_arg(*ap, double), width);
+ i += 2;
+#if MJSON_ENABLE_BASE64
+ } else if (fc == 'V') {
+ int len = va_arg(*ap, int);
+ const char* buf = va_arg(*ap, const char*);
+ n += mjson_print_b64(fn, fnd, (const unsigned char*)buf, len);
+#endif
+ } else if (fc == 'H') {
+ const char* hex = "0123456789abcdef";
+ int j, len = va_arg(*ap, int);
+ const unsigned char* p = va_arg(*ap, const unsigned char*);
+ n += fn("\"", 1, fnd);
+ for (j = 0; j < len; j++) {
+ n += fn(&hex[(p[j] >> 4) & 15], 1, fnd);
+ n += fn(&hex[p[j] & 15], 1, fnd);
+ }
+ n += fn("\"", 1, fnd);
+ } else if (fc == 'M') {
+ mjson_vprint_fn_t vfn = va_arg(*ap, mjson_vprint_fn_t);
+ n += vfn(fn, fnd, ap);
+ }
+ i++;
+ } else {
+ n += mjson_print_buf(fn, fnd, &fmt[i++], 1);
+ }
+ }
+ return n;
+}
+
+int
+mjson_printf(mjson_print_fn_t fn, void* fnd, const char* fmt, ...)
+{
+ va_list ap;
+ int len;
+ va_start(ap, fmt);
+ len = mjson_vprintf(fn, fnd, fmt, &ap);
+ va_end(ap);
+ return len;
+}
+#endif /* MJSON_ENABLE_PRINT */
+
+static int
+is_digit(int c)
+{
+ return c >= '0' && c <= '9';
+}
+
+/* NOTE: strtod() implementation by Yasuhiro Matsumoto. */
+static double
+mystrtod(const char* str, const char** end)
+{
+ double d = 0.0;
+ int sign = 1, n = 0;
+ const char *p = str, *a = str;
+
+ /* decimal part */
+ if (*p == '-') {
+ sign = -1;
+ ++p;
+ } else if (*p == '+') {
+ ++p;
+ }
+ if (is_digit(*p)) {
+ d = (double)(*p++ - '0');
+ while (*p && is_digit(*p)) {
+ d = d * 10.0 + (double)(*p - '0');
+ ++p;
+ ++n;
+ }
+ a = p;
+ } else if (*p != '.') {
+ goto done;
+ }
+ d *= sign;
+
+ /* fraction part */
+ if (*p == '.') {
+ double f = 0.0;
+ double base = 0.1;
+ ++p;
+
+ if (is_digit(*p)) {
+ while (*p && is_digit(*p)) {
+ f += base * (*p - '0');
+ base /= 10.0;
+ ++p;
+ ++n;
+ }
+ }
+ d += f * sign;
+ a = p;
+ }
+
+ /* exponential part */
+ if ((*p == 'E') || (*p == 'e')) {
+ int i, e = 0, neg = 0;
+ p++;
+ if (*p == '-')
+ p++, neg++;
+ if (*p == '+')
+ p++;
+ while (is_digit(*p))
+ e = e * 10 + *p++ - '0';
+ if (neg)
+ e = -e;
+#if 0
+ if (d == 2.2250738585072011 && e == -308) {
+ d = 0.0;
+ a = p;
+ goto done;
+ }
+ if (d == 2.2250738585072012 && e <= -308) {
+ d *= 1.0e-308;
+ a = p;
+ goto done;
+ }
+#endif
+ for (i = 0; i < e; i++)
+ d *= 10;
+ for (i = 0; i < -e; i++)
+ d /= 10;
+ a = p;
+ } else if (p > str && !is_digit(*(p - 1))) {
+ a = str;
+ goto done;
+ }
+
+done:
+ if (end)
+ *end = a;
+ return d;
+}
+
+#if MJSON_ENABLE_MERGE
+int
+mjson_merge(const char* s,
+ int n,
+ const char* s2,
+ int n2,
+ mjson_print_fn_t fn,
+ void* userdata)
+{
+ int koff, klen, voff, vlen, t, t2, k, off = 0, len = 0, comma = 0;
+ if (n < 2)
+ return len;
+ len += fn("{", 1, userdata);
+ while ((off = mjson_next(s, n, off, &koff, &klen, &voff, &vlen, &t)) != 0) {
+ char* path = (char*)alloca((size_t)klen + 1);
+ const char* val;
+ memcpy(path, "$.", 2);
+ memcpy(path + 2, s + koff + 1, (size_t)(klen - 2));
+ path[klen] = '\0';
+ if ((t2 = mjson_find(s2, n2, path, &val, &k)) != MJSON_TOK_INVALID) {
+ if (t2 == MJSON_TOK_NULL)
+ continue; // null deletes the key
+ } else {
+ val = s + voff; // Key is not found in the update. Copy the old value.
+ }
+ if (comma)
+ len += fn(",", 1, userdata);
+ len += fn(s + koff, klen, userdata);
+ len += fn(":", 1, userdata);
+ if (t == MJSON_TOK_OBJECT && t2 == MJSON_TOK_OBJECT) {
+ len += mjson_merge(s + voff, vlen, val, k, fn, userdata);
+ } else {
+ if (t2 != MJSON_TOK_INVALID)
+ vlen = k;
+ len += fn(val, vlen, userdata);
+ }
+ comma = 1;
+ }
+ // Add missing keys
+ off = 0;
+ while ((off = mjson_next(s2, n2, off, &koff, &klen, &voff, &vlen, &t)) != 0) {
+ char* path = (char*)alloca((size_t)klen + 1);
+ const char* val;
+ if (t == MJSON_TOK_NULL)
+ continue;
+ memcpy(path, "$.", 2);
+ memcpy(path + 2, s2 + koff + 1, (size_t)(klen - 2));
+ path[klen] = '\0';
+ if (mjson_find(s, n, path, &val, &vlen) != MJSON_TOK_INVALID)
+ continue;
+ if (comma)
+ len += fn(",", 1, userdata);
+ len += fn(s2 + koff, klen, userdata);
+ len += fn(":", 1, userdata);
+ len += fn(s2 + voff, vlen, userdata);
+ comma = 1;
+ }
+ len += fn("}", 1, userdata);
+ return len;
+}
+#endif // MJSON_ENABLE_MERGE
+
+#if MJSON_ENABLE_PRETTY
+struct prettydata
+{
+ int level;
+ int len;
+ int prev;
+ const char* pad;
+ int padlen;
+ mjson_print_fn_t fn;
+ void* userdata;
+};
+
+static int
+pretty_cb(int ev, const char* s, int off, int len, void* ud)
+{
+ struct prettydata* d = (struct prettydata*)ud;
+ int i;
+ switch (ev) {
+ case '{':
+ case '[':
+ d->level++;
+ d->len += d->fn(s + off, len, d->userdata);
+ break;
+ case '}':
+ case ']':
+ d->level--;
+ if (d->prev != '[' && d->prev != '{' && d->padlen > 0) {
+ d->len += d->fn("\n", 1, d->userdata);
+ for (i = 0; i < d->level; i++)
+ d->len += d->fn(d->pad, d->padlen, d->userdata);
+ }
+ d->len += d->fn(s + off, len, d->userdata);
+ break;
+ case ',':
+ d->len += d->fn(s + off, len, d->userdata);
+ if (d->padlen > 0) {
+ d->len += d->fn("\n", 1, d->userdata);
+ for (i = 0; i < d->level; i++)
+ d->len += d->fn(d->pad, d->padlen, d->userdata);
+ }
+ break;
+ case ':':
+ d->len += d->fn(s + off, len, d->userdata);
+ if (d->padlen > 0)
+ d->len += d->fn(" ", 1, d->userdata);
+ break;
+ case MJSON_TOK_KEY:
+ if (d->prev == '{' && d->padlen > 0) {
+ d->len += d->fn("\n", 1, d->userdata);
+ for (i = 0; i < d->level; i++)
+ d->len += d->fn(d->pad, d->padlen, d->userdata);
+ }
+ d->len += d->fn(s + off, len, d->userdata);
+ break;
+ default:
+ if (d->prev == '[' && d->padlen > 0) {
+ d->len += d->fn("\n", 1, d->userdata);
+ for (i = 0; i < d->level; i++)
+ d->len += d->fn(d->pad, d->padlen, d->userdata);
+ }
+ d->len += d->fn(s + off, len, d->userdata);
+ break;
+ }
+ d->prev = ev;
+ return 0;
+}
+
+int
+mjson_pretty(const char* s,
+ int n,
+ const char* pad,
+ mjson_print_fn_t fn,
+ void* userdata)
+{
+ struct prettydata d = { 0, 0, 0, pad, (int)strlen(pad), fn, userdata };
+ if (mjson(s, n, pretty_cb, &d) < 0)
+ return -1;
+ return d.len;
+}
+#endif // MJSON_ENABLE_PRETTY
+
+#if MJSON_ENABLE_RPC
+struct jsonrpc_ctx jsonrpc_default_context;
+
+int
+mjson_globmatch(const char* s1, int n1, const char* s2, int n2)
+{
+ int i = 0, j = 0, ni = 0, nj = 0;
+ while (i < n1 || j < n2) {
+ if (i < n1 && j < n2 && (s1[i] == '?' || s2[j] == s1[i])) {
+ i++, j++;
+ } else if (i < n1 && (s1[i] == '*' || s1[i] == '#')) {
+ ni = i, nj = j + 1, i++;
+ } else if (nj > 0 && nj <= n2 && (s1[i - 1] == '#' || s2[j] != '/')) {
+ i = ni, j = nj;
+ } else {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void
+jsonrpc_return_errorv(struct jsonrpc_request* r,
+ int code,
+ const char* message,
+ const char* data_fmt,
+ va_list* ap)
+{
+ mjson_printf(r->fn, r->fn_data, "{\"jsonrpc\":\"2.0\",\"id\":");
+ if (r->id_len > 0 && r->id != NULL) {
+ mjson_printf(r->fn, r->fn_data, "%.*s", r->id_len, r->id);
+ } else {
+ mjson_printf(r->fn, r->fn_data, "null");
+ }
+ mjson_printf(r->fn,
+ r->fn_data,
+ ",\"error\":{\"code\":%d,\"message\":%Q",
+ code,
+ message == NULL ? "" : message);
+ if (data_fmt != NULL) {
+ mjson_printf(r->fn, r->fn_data, ",\"data\":");
+ mjson_vprintf(r->fn, r->fn_data, data_fmt, ap);
+ }
+ // LF NEWLINE NL
+ mjson_printf(r->fn, r->fn_data, "}}\n");
+ /* mjson_printf(r->fn, r->fn_data, "}}"); */
+}
+
+void
+jsonrpc_return_error(struct jsonrpc_request* r,
+ int code,
+ const char* message,
+ const char* data_fmt,
+ ...)
+{
+ va_list ap;
+ va_start(ap, data_fmt);
+ jsonrpc_return_errorv(r, code, message, data_fmt, &ap);
+ va_end(ap);
+}
+
+void
+jsonrpc_return_successv(struct jsonrpc_request* r,
+ const char* result_fmt,
+ va_list* ap)
+{
+ if (r->id_len == 0)
+ return;
+ mjson_printf(r->fn, r->fn_data, "{\"jsonrpc\":\"2.0\",\"id\":%.*s,\"result\":", r->id_len, r->id);
+ if (result_fmt != NULL) {
+ mjson_vprintf(r->fn, r->fn_data, result_fmt, ap);
+ } else {
+ mjson_printf(r->fn, r->fn_data, "%s", "null");
+ }
+ // LF NEWLINE NL
+ mjson_printf(r->fn, r->fn_data, "}");
+}
+
+
+void
+jsonrpc_return_successv2(struct jsonrpc_request* r, bool add_endl,
+ const char* result_fmt,
+ va_list* ap)
+{
+ if (r->id_len == 0)
+ return; // TODO assert
+ mjson_printf(r->fn, r->fn_data, "{\"jsonrpc\":\"2.0\",\"id\":%.*s,\"result\":", r->id_len, r->id);
+ if (result_fmt != NULL) {
+ mjson_vprintf(r->fn, r->fn_data, result_fmt, ap);
+ } else {
+ mjson_printf(r->fn, r->fn_data, "%s", "null");
+ }
+ // LF NEWLINE NL
+ mjson_printf (r->fn, r->fn_data, "}");
+ if (add_endl)
+ {
+ mjson_printf(r->fn, r->fn_data, "\n");
+ }
+}
+
+void
+jsonrpc_return_success2(struct jsonrpc_request* r, bool add_endl, const char* result_fmt, ...)
+{
+ va_list ap;
+ va_start(ap, result_fmt);
+ jsonrpc_return_successv2(r, add_endl, result_fmt, &ap);
+ va_end(ap);
+}
+
+
+void
+jsonrpc_return_success(struct jsonrpc_request* r, const char* result_fmt, ...)
+{
+ va_list ap;
+ va_start(ap, result_fmt);
+ jsonrpc_return_successv(r, result_fmt, &ap);
+ va_end(ap);
+}
+
+void
+jsonrpc_ctx_process(struct jsonrpc_ctx* ctx,
+ const char* buf,
+ int len,
+ mjson_print_fn_t fn,
+ void* fn_data,
+ void* ud)
+{
+ const char *result = NULL, *error = NULL;
+ int result_sz = 0, error_sz = 0;
+ struct jsonrpc_method* m = NULL;
+ struct jsonrpc_request r = {
+ ctx, buf, len, 0, 0, 0, 0, 0, 0, fn, fn_data, ud
+ };
+
+ // Is is a response frame?
+ mjson_find(buf, len, "$.result", &result, &result_sz);
+ if (result == NULL)
+ mjson_find(buf, len, "$.error", &error, &error_sz);
+ if (result_sz > 0 || error_sz > 0) {
+ if (ctx->response_cb)
+ ctx->response_cb(buf, len, ctx->response_cb_data);
+ return;
+ }
+
+ // Method must exist and must be a string
+ if (mjson_find(buf, len, "$.method", &r.method, &r.method_len) !=
+ MJSON_TOK_STRING) {
+ mjson_printf(fn,
+ fn_data,
+ "{\"error\":{\"code\":-32700,\"message\":%.*Q}}\n",
+ len,
+ buf);
+ return;
+ }
+
+ // id and params are optional
+ mjson_find(buf, len, "$.id", &r.id, &r.id_len);
+ mjson_find(buf, len, "$.params", &r.params, &r.params_len);
+
+ for (m = ctx->methods; m != NULL; m = m->next) {
+ if (mjson_globmatch(
+ m->method, m->method_sz, r.method + 1, r.method_len - 2) > 0) {
+ if (r.params == NULL)
+ r.params = "";
+ m->cb(&r);
+ break;
+ }
+ }
+ if (m == NULL) {
+ jsonrpc_return_error(&r, JSONRPC_ERROR_NOT_FOUND, "method not found", NULL);
+ }
+}
+
+static int
+jsonrpc_print_methods(mjson_print_fn_t fn, void* fn_data, va_list* ap)
+{
+ struct jsonrpc_ctx* ctx = va_arg(*ap, struct jsonrpc_ctx*);
+ struct jsonrpc_method* m;
+ int len = 0;
+ for (m = ctx->methods; m != NULL; m = m->next) {
+ if (m != ctx->methods)
+ len += mjson_print_buf(fn, fn_data, ",", 1);
+ len += mjson_print_str(fn, fn_data, m->method, (int)strlen(m->method));
+ }
+ return len;
+}
+
+void
+jsonrpc_list(struct jsonrpc_request* r)
+{
+ jsonrpc_return_success(r, "[%M]", jsonrpc_print_methods, r->ctx);
+}
+
+void
+jsonrpc_ctx_init(struct jsonrpc_ctx* ctx,
+ mjson_print_fn_t response_cb,
+ void* response_cb_data)
+{
+ ctx->methods = NULL;
+ ctx->response_cb = response_cb;
+ ctx->response_cb_data = response_cb_data;
+}
+
+void
+jsonrpc_init(mjson_print_fn_t response_cb, void* userdata)
+{
+ struct jsonrpc_ctx* ctx = &jsonrpc_default_context;
+ jsonrpc_ctx_init(ctx, response_cb, userdata);
+ jsonrpc_ctx_export(ctx, MJSON_RPC_LIST_NAME, jsonrpc_list);
+}
+#endif // MJSON_ENABLE_RPC
diff --git a/third_party/mcpc/src/mjson.h b/third_party/mcpc/src/mjson.h
new file mode 100644
index 0000000..7a562f0
--- /dev/null
+++ b/third_party/mcpc/src/mjson.h
@@ -0,0 +1,296 @@
+// Copyright (c) 2018-2020 Cesanta Software Limited
+// All rights reserved
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+#ifndef MJSON_H
+#define MJSON_H
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mcpc/_c23_uchar.h>
+#include <mcpc/_c23_keywords.h>
+#include <stdint.h>
+
+#ifndef MJSON_ENABLE_PRINT
+#define MJSON_ENABLE_PRINT 1
+#endif
+
+#ifndef MJSON_ENABLE_RPC
+#define MJSON_ENABLE_RPC 1
+#endif
+
+#ifndef MJSON_ENABLE_BASE64
+#define MJSON_ENABLE_BASE64 1
+#endif
+
+#ifndef MJSON_ENABLE_MERGE
+#define MJSON_ENABLE_MERGE 1
+#endif
+
+#ifndef MJSON_ENABLE_PRETTY
+#define MJSON_ENABLE_PRETTY 1
+#endif
+
+#ifndef MJSON_ENABLE_NEXT
+#define MJSON_ENABLE_NEXT 1
+#endif
+
+#ifndef MJSON_RPC_LIST_NAME
+#define MJSON_RPC_LIST_NAME "rpc.list"
+#endif
+
+#ifndef MJSON_DYNBUF_CHUNK
+#define MJSON_DYNBUF_CHUNK 256 // Allocation granularity for print_dynamic_buf
+#endif
+
+#ifndef MJSON_REALLOC
+#define MJSON_REALLOC realloc
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define MJSON_ERROR_INVALID_INPUT (-1)
+#define MJSON_ERROR_TOO_DEEP (-2)
+#define MJSON_TOK_INVALID 0
+#define MJSON_TOK_KEY 1
+#define MJSON_TOK_STRING 11
+#define MJSON_TOK_NUMBER 12
+#define MJSON_TOK_TRUE 13
+#define MJSON_TOK_FALSE 14
+#define MJSON_TOK_NULL 15
+#define MJSON_TOK_ARRAY 91
+#define MJSON_TOK_OBJECT 123
+#define MJSON_TOK_IS_VALUE(t) ((t) > 10 && (t) < 20)
+
+ typedef int (*mjson_cb_t)(int event,
+ const char* buf,
+ int offset,
+ int len,
+ void* fn_data);
+
+#ifndef MJSON_MAX_DEPTH
+#define MJSON_MAX_DEPTH 20
+#endif
+
+ int mjson(const char* buf, int len, mjson_cb_t cb, void* ud);
+ int mjson_find(const char* buf,
+ int len,
+ const char* jp,
+ const char** tp,
+ int* tl);
+ int32_t mjson_find8(const char8_t* buf,
+ int32_t len,
+ const char8_t* jp,
+ const char8_t** tp,
+ int32_t* tl);
+ int32_t mjson_find88(const char* buf,
+ int32_t len,
+ const char8_t* jp,
+ const char8_t** tp,
+ int32_t* tl);
+ int32_t mjson_find88(const char* buf,
+ int32_t len,
+ const char8_t* jp,
+ const char8_t** tp,
+ int32_t* tl);
+ int mjson_get_number(const char* buf, int len, const char* path, double* v);
+ int mjson_get_bool(const char* buf, int len, const char* path, int* v);
+ int mjson_get_string(const char* buf,
+ int len,
+ const char* path,
+ char* to,
+ int n);
+ int mjson_get_hex(const char* buf,
+ int len,
+ const char* path,
+ char* to,
+ int n);
+
+#if MJSON_ENABLE_NEXT
+ int mjson_next(const char* buf,
+ int len,
+ int offset,
+ int* key_offset,
+ int* key_len,
+ int* val_offset,
+ int* val_len,
+ int* vale_type);
+#endif
+
+#if MJSON_ENABLE_BASE64
+ int mjson_get_base64(const char* buf,
+ int len,
+ const char* path,
+ char* dst,
+ int dst_len);
+ int mjson_base64_dec(const char* src, int src_len, char* dst, int dst_len);
+#endif
+
+#if MJSON_ENABLE_PRINT
+ typedef int (*mjson_print_fn_t)(const char* buf, int len, void* fn_data);
+ typedef int (*mjson_vprint_fn_t)(mjson_print_fn_t fn, void*, va_list*);
+
+ struct mjson_fixedbuf
+ {
+ char* ptr;
+ int size, len;
+ };
+
+ int mjson_printf(mjson_print_fn_t fn, void* fn_data, const char* fmt, ...);
+ int mjson_vprintf(mjson_print_fn_t fn,
+ void* fn_data,
+ const char* fmt,
+ va_list* ap);
+ int mjson_print_str(mjson_print_fn_t fn,
+ void* fn_data,
+ const char* buf,
+ int len);
+ int mjson_print_int(mjson_print_fn_t fn,
+ void* fn_data,
+ int value,
+ int is_signed);
+ int mjson_print_long(mjson_print_fn_t fn,
+ void* fn_data,
+ long value,
+ int is_signed);
+ int mjson_print_buf(mjson_print_fn_t fn,
+ void* fn_data,
+ const char* buf,
+ int len);
+ int mjson_print_dbl(mjson_print_fn_t fn, void* fn_data, double d, int width);
+
+ int mjson_print_null(const char* ptr, int len, void* fn_data);
+ int mjson_print_fixed_buf(const char* ptr, int len, void* fn_data);
+ int mjson_print_dynamic_buf(const char* ptr, int len, void* fn_data);
+
+ int mjson_snprintf(char* buf, size_t len, const char* fmt, ...);
+ char* mjson_aprintf(const char* fmt, ...);
+
+#if MJSON_ENABLE_PRETTY
+ int mjson_pretty(const char* s,
+ int n,
+ const char* pad,
+ mjson_print_fn_t fn,
+ void* fn_data);
+#endif
+
+#if MJSON_ENABLE_MERGE
+ int mjson_merge(const char* s,
+ int n,
+ const char* s2,
+ int n2,
+ mjson_print_fn_t fn,
+ void* fn_data);
+#endif
+
+#endif // MJSON_ENABLE_PRINT
+
+#if MJSON_ENABLE_RPC
+
+ void jsonrpc_init(mjson_print_fn_t response_cb, void* fn_data);
+ int mjson_globmatch(const char* s1, int n1, const char* s2, int n2);
+
+ struct jsonrpc_request
+ {
+ struct jsonrpc_ctx* ctx;
+ const char* frame; // Points to the whole frame
+ int frame_len; // Frame length
+ const char* params; // Points to the "params" in the request frame
+ int params_len; // Length of the "params"
+ const char* id; // Points to the "id" in the request frame
+ int id_len; // Length of the "id"
+ const char* method; // Points to the "method" in the request frame
+ int method_len; // Length of the "method"
+ mjson_print_fn_t fn; // Printer function
+ void* fn_data; // Printer function data
+ void* userdata; // Callback's user data as specified at export time
+ };
+
+ struct jsonrpc_method
+ {
+ const char* method;
+ int method_sz;
+ void (*cb)(struct jsonrpc_request*);
+ struct jsonrpc_method* next;
+ };
+
+ // Main RPC context, stores current request information and a list of
+ // exported RPC methods.
+ struct jsonrpc_ctx
+ {
+ struct jsonrpc_method* methods;
+ mjson_print_fn_t response_cb;
+ void* response_cb_data;
+ };
+
+// Registers function fn under the given name within the given RPC context
+#define jsonrpc_ctx_export(ctx, name, fn) \
+ do { \
+ static struct jsonrpc_method m = { (name), sizeof(name) - 1, (fn), 0 }; \
+ m.next = (ctx)->methods; \
+ (ctx)->methods = &m; \
+ } while (0)
+
+ void jsonrpc_ctx_init(struct jsonrpc_ctx* ctx,
+ mjson_print_fn_t response_cb,
+ void* response_cb_data);
+ void jsonrpc_return_error(struct jsonrpc_request* r,
+ int code,
+ const char* message,
+ const char* data_fmt,
+ ...);
+ void jsonrpc_return_success(struct jsonrpc_request* r,
+ const char* result_fmt,
+ ...);
+ void jsonrpc_return_success2(struct jsonrpc_request* r, bool add_endl,
+ const char* result_fmt,
+ ...);
+ void jsonrpc_ctx_process(struct jsonrpc_ctx* ctx,
+ const char* req,
+ int req_sz,
+ mjson_print_fn_t fn,
+ void* fn_data,
+ void* userdata);
+
+ extern struct jsonrpc_ctx jsonrpc_default_context;
+ extern void jsonrpc_list(struct jsonrpc_request* r);
+
+#define jsonrpc_export(name, fn) \
+ jsonrpc_ctx_export(&jsonrpc_default_context, (name), (fn))
+
+#define jsonrpc_process(buf, len, fn, fnd, ud) \
+ jsonrpc_ctx_process(&jsonrpc_default_context, (buf), (len), (fn), (fnd), (ud))
+
+#define JSONRPC_ERROR_INVALID -32700 /* Invalid JSON was received */
+#define JSONRPC_ERROR_INVALID_REQUEST -32600 /* The JSON-RPC request is invalid */
+#define JSONRPC_ERROR_NOT_FOUND -32601 /* The method does not exist */
+#define JSONRPC_ERROR_BAD_PARAMS -32602 /* Invalid params passed */
+#define JSONRPC_ERROR_INTERNAL -32603 /* Internal JSON-RPC error */
+
+#endif // MJSON_ENABLE_RPC
+#ifdef __cplusplus
+}
+#endif
+#endif // MJSON_H
diff --git a/third_party/mcpc/src/prmpt.c b/third_party/mcpc/src/prmpt.c
new file mode 100644
index 0000000..324e5dd
--- /dev/null
+++ b/third_party/mcpc/src/prmpt.c
@@ -0,0 +1,378 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include "prmpt.h"
+
+#include "ext_string.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include "errcode.h"
+#include "alloc.h"
+#include "anydata.h"
+
+/* = = = = = = = = = = = = = = = prmptarg = = = = = = = = = = = = = = = */
+
+MCPC_API mcpc_prmptarg_t *
+mcpc_prmptarg_new (const char8_t *name, const size_t name_len, const char8_t *desc, const size_t desc_len)
+{
+ mcpc_anydata_t *hp = mcpc_anydata_new_meta1_meta2 (name, name_len, desc, desc_len);
+
+ hp->flags |= ANYDATA_FL_REQUIRED;
+ hp->flags |= ANYDATA_FL_PRMPTARG;
+
+ hp->data_typ = MCPC_U8STR;
+
+ return hp;
+}
+
+MCPC_API mcpc_prmptarg_t *
+mcpc_prmptarg_new2 (const char8_t *nament, const char8_t *descnt)
+{
+ return mcpc_prmptarg_new (nament, u8strlen (nament), descnt, u8strlen (descnt));
+}
+
+MCPC_API mcpc_prmptarg_t *
+mcpc_prmptarg_new3 (const char8_t *nament)
+{
+ return mcpc_prmptarg_new (nament, u8strlen (nament), nullptr, 0);
+}
+
+void
+mcpc_prmptarg_free (mcpc_prmptarg_t *prmptarg)
+{
+ mcpc_anydata_free ((mcpc_anydata_t *) prmptarg);
+}
+
+bool
+mcpc_prmptarg_is_required (const mcpc_prmptarg_t *prmptarg)
+{
+ return prmptarg->flags & ANYDATA_FL_REQUIRED;
+}
+
+mcpc_errcode_t
+mcpc_prmptarg_getref_name (const mcpc_prmptarg_t *prmptarg, const char8_t **ret, size_t *ret_len)
+{
+ return mcpc_anydata_getref_meta1 (prmptarg, ret, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_prmptarg_getref_desc (const mcpc_prmptarg_t *prmptarg, const char8_t **ret, size_t *ret_len)
+{
+ return mcpc_anydata_getref_meta2 (prmptarg, ret, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_prmptarg_get_data_type (const mcpc_prmptarg_t *prmptarg, mcpc_anytype_t *ret)
+{
+ bug_if_nullptr (prmptarg);
+ bug_if_nullptr (ret);
+ *ret = ((const mcpc_anydata_t *) prmptarg)->data_typ;
+ return MCPC_EC_0;
+}
+
+mcpc_errcode_t
+mcpc_prmptarg_set_required (mcpc_prmptarg_t *prmptarg, bool fl)
+{
+ bug_if_nullptr (prmptarg);
+
+ if (fl)
+ {
+ prmptarg->flags |= ANYDATA_FL_REQUIRED;
+ }
+ else
+ {
+ prmptarg->flags &= (~ANYDATA_FL_REQUIRED);
+ }
+
+ return MCPC_EC_0;
+}
+
+mcpc_errcode_t
+mcpc_prmptarg_set_nament (mcpc_prmptarg_t *prmptarg, const char8_t *name)
+{
+ return mcpc_anydata_set_meta1nt (prmptarg, name);
+}
+
+mcpc_errcode_t
+mcpc_prmptarg_set_u8str (mcpc_prmptarg_t *prmptarg, const char8_t *arr, size_t arr_len)
+{
+ return mcpc_anydata_set_u8str (prmptarg, arr, arr_len);
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_prmptarg_get_u8str (const mcpc_prmptarg_t *prmptarg, char8_t *ret, size_t ret_cap, size_t *ret_len)
+{
+ return mcpc_anydata_get_u8str (prmptarg, ret, ret_cap, ret_len);
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_prmptarg_getref_u8str (const mcpc_prmptarg_t *prmptarg, const char8_t **ret, size_t *ret_len)
+{
+ return mcpc_anydata_getref_u8str (prmptarg, ret, ret_len);
+}
+
+/* = = = = = = = = = = = = = = = = = prmpt = = = = = = = = = = = = = = = = = */
+
+MCPC_API mcpc_prmpt_t *
+mcpc_prmpt_new (const char8_t *name, const size_t name_len, const char8_t *desc, const size_t desc_len)
+{
+ mcpc_anydata_t *hp = mcpc_anydata_new_meta1_meta2 (name, name_len, desc, desc_len);
+
+ hp->flags |= ANYDATA_FL_PRMPT;
+
+ return hp;
+}
+
+MCPC_API mcpc_prmpt_t *
+mcpc_prmpt_new2 (const char8_t *nament, const char8_t *descnt)
+{
+ return mcpc_prmpt_new (nament, strlen ((const char *) nament), descnt, strlen ((const char *) descnt));
+}
+
+MCPC_API mcpc_prmpt_t *
+mcpc_prmpt_new3 (const char8_t *nament)
+{
+ return mcpc_prmpt_new (nament, u8strlen (nament), nullptr, 0);
+}
+
+void
+mcpc_prmpt_free (mcpc_prmpt_t *prmpt)
+{
+ mcpc_anydata_free ((mcpc_anydata_t *) prmpt);
+}
+
+mcpc_errcode_t
+mcpc_prmpt_getref_name (const mcpc_prmpt_t *prmpt, const char8_t **ret, size_t *ret_len)
+{
+ return mcpc_anydata_getref_meta1 (prmpt, ret, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_prmpt_getref_desc (const mcpc_prmpt_t *prmpt, const char8_t **ret, size_t *ret_len)
+{
+ return mcpc_anydata_getref_meta2 (prmpt, ret, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_prmpt_get_callcb (const mcpc_prmpt_t *prmpt, mcpc_prmpt_callcb_t *ret)
+{
+ return mcpc_anydata_get_func (prmpt, ret);
+}
+
+mcpc_errcode_t
+mcpc_prmpt_set_callcb (mcpc_prmpt_t *prmpt, mcpc_prmpt_callcb_t cb)
+{
+ mcpc_anydata_set_func (prmpt, cb);
+ return MCPC_EC_0;
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_prmpt_addfre_prmptarg (mcpc_prmpt_t *prmpt, mcpc_prmptarg_t *prmptarg)
+{
+ mcpc_anydata_add_child (prmpt, prmptarg);
+ return MCPC_EC_0;
+
+}
+
+MCPC_API void
+mcpc_prmpt_addcpy_prmptarg (mcpc_prmpt_t *prmpt, mcpc_prmptarg_t *prmptarg)
+{
+ mcpc_anydata_addcpy_child (prmpt, prmptarg); // TODO return EC
+}
+
+MCPC_API const mcpc_prmptarg_t *
+mcpc_prmpt_get_prmptarg (const mcpc_prmpt_t *prmpt, size_t idx)
+{
+ return mcpc_anydata_get_child (prmpt, idx);
+}
+
+MCPC_API size_t
+mcpc_prmpt_get_prmptarg_len (const mcpc_prmpt_t *prmpt)
+{
+ bug_if_nullptr (prmpt);
+ mcpc_anydata_t *prmpt_imp = (mcpc_anydata_t *) prmpt;
+ if (prmpt_imp->chd_pool == nullptr)
+ return 0;
+ return prmpt_imp->chd_pool->len;
+}
+
+MCPC_API const mcpc_prmptarg_t *
+mcpc_prmpt_get_next_prmptarg (const mcpc_prmpt_t *prmpt)
+{
+ bug_if_nullptr (prmpt);
+
+ static size_t idx = 0;
+
+ const mcpc_anypool_t *pool = prmpt->chd_pool;
+ if (pool == nullptr)
+ return nullptr;
+
+ const size_t pool_len = pool->len;
+ if (pool_len == 0)
+ return nullptr;
+
+ if (idx == pool_len) // TODO MT-SAFE
+ {
+ idx = 0;
+ return nullptr;
+ }
+
+ const mcpc_anydata_t *ret = pool->head + idx;
+ idx += 1;
+
+ return ret;
+}
+
+MCPC_API const mcpc_prmptarg_t *
+mcpc_prmpt_getbyname_prmptarg (const mcpc_prmpt_t *prmpt, const char8_t *parg_name, size_t parg_name_len)
+{
+ const mcpc_anydata_t *chd = mcpc_anypool_getbymeta1 (prmpt->chd_pool, parg_name, parg_name_len);
+ return (mcpc_prmptarg_t *) chd;
+}
+
+MCPC_API const mcpc_prmptarg_t *
+mcpc_prmpt_getbynament_prmptarg (const mcpc_prmpt_t *prmpt, const char8_t *parg_nament)
+{
+ const mcpc_anydata_t *chd = mcpc_anypool_getbymeta1nt (prmpt->chd_pool, parg_nament);
+ return (mcpc_prmptarg_t *) chd;
+}
+
+/* = = = = = = = = = = = = = = = prmptpool = = = = = = = = = = = = = = = */
+
+mcpc_prmptpool_t *
+mcpc_prmptpool_new ()
+{
+ return mcpc_anypool_new ();
+}
+
+void
+mcpc_prmptpool_free (mcpc_prmptpool_t *prmptpool)
+{
+ mcpc_anypool_free (prmptpool);
+}
+
+void
+mcpc_prmptpool_addcpy (mcpc_prmptpool_t *prmptpool, const mcpc_prmpt_t *prmpt)
+{
+ mcpc_anypool_addcpy (prmptpool, prmpt);
+}
+
+void
+mcpc_prmptpool_addfre (mcpc_prmptpool_t *prmptpool, mcpc_prmpt_t *prmpt)
+{
+ mcpc_anypool_addfre (prmptpool, prmpt);
+}
+
+size_t
+mcpc_prmptpool_get_len (const mcpc_prmptpool_t *prmptpool)
+{
+ bug_if_nullptr (prmptpool);
+ size_t ret = 0;
+ ret = ((const mcpc_anypool_t *) prmptpool)->len;
+ return ret;
+}
+
+const mcpc_prmpt_t *
+mcpc_prmptpool_get (const mcpc_prmptpool_t *prmptpool, size_t idx)
+{
+ return mcpc_anypool_get (prmptpool, idx);
+}
+
+const mcpc_prmpt_t *
+mcpc_prmptpool_getbyname (const mcpc_prmptpool_t *prmptpool, const char8_t *name, size_t name_len)
+{
+ return mcpc_anypool_getbymeta1 (prmptpool, name, name_len);
+}
+
+/* -= = = = = = = = = = = = = = prmptarghint = = = = = = = = = = = = = = = */
+
+mcpc_prmptarghint_t *
+mcpc_prmptarghint_new ()
+{
+ mcpc_anydata_t *anydata = mcpc_anydata_new_typ (MCPC_U8STR);
+ anydata->flags |= ANYDATA_FL_PRMPTARGHINT;
+ return anydata;
+}
+
+mcpc_prmptarghint_t *
+mcpc_prmptarghint_new_vprintf8 (const char8_t *fmt, va_list *vargs)
+{
+ char8_t *sbuf = mcpc_alloc (1024);
+ int printed = vsprintf ((char *) sbuf, (const char *) fmt, *vargs);
+
+ mcpc_prmptarghint_t *prmptarghint = mcpc_prmptarghint_new ();
+ bug_if_nz (mcpc_anydata_set_u8str (prmptarghint, sbuf, (size_t) printed));
+
+ free (sbuf);
+ return prmptarghint;
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_prmptarg_add_hint_printf8 (mcpc_prmptarg_t *prmptarg, const char8_t *fmt, ...)
+{
+ va_list vargs;
+ va_start (vargs, fmt);
+
+ mcpc_prmptarghint_t *hint = mcpc_prmptarghint_new_vprintf8 (fmt, &vargs);
+
+ if (prmptarg->chd_pool == nullptr)
+ prmptarg->chd_pool = mcpc_anypool_new ();
+
+ mcpc_anypool_addfre (prmptarg->chd_pool, hint);
+
+ va_end (vargs);
+ return MCPC_EC_0;
+}
+
+mcpc_errcode_t
+mcpc_prmptarghint_getref_u8str (const mcpc_prmptarg_t *prmptarghint, const char8_t **ret, size_t *ret_len)
+{
+ return mcpc_anydata_getref_u8str (prmptarghint, ret, ret_len);
+}
+
+const mcpc_prmptarghint_t *
+mcpc_prmptarg_get_next_prmptarghint (const mcpc_prmptarg_t *prmptarg)
+{
+ bug_if_nullptr (prmptarg);
+
+ static size_t idx = 0;
+
+ const mcpc_anypool_t *pool = prmptarg->chd_pool;
+ if (pool == nullptr)
+ return nullptr;
+
+ const size_t pool_len = pool->len;
+ if (pool_len == 0)
+ return nullptr;
+
+ if (idx == pool_len) // TODO MT-SAFE
+ {
+ idx = 0;
+ return nullptr;
+ }
+
+ const mcpc_anydata_t *ret = pool->head + idx;
+ idx += 1;
+
+ return ret;
+}
diff --git a/third_party/mcpc/src/prmpt.h b/third_party/mcpc/src/prmpt.h
new file mode 100644
index 0000000..2199864
--- /dev/null
+++ b/third_party/mcpc/src/prmpt.h
@@ -0,0 +1,80 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h_prmpt
+#define h_prmpt
+
+#include <mcpc/prmpt.h>
+
+typedef mcpc_anypool_t mcpc_prmptpool_t;
+typedef mcpc_anypool_t mcpc_prmptargpool_t;
+
+// ---------------------------- prmptarg ----------------------------
+
+void mcpc_prmptarg_free (mcpc_prmptarg_t * prmptarg);
+
+bool mcpc_prmptarg_is_required (const mcpc_prmptarg_t * prmptarg);
+
+mcpc_errcode_t mcpc_prmptarg_getref_name (const mcpc_prmptarg_t * prmptarg, const char8_t ** ret, size_t *ret_len);
+
+mcpc_errcode_t mcpc_prmptarg_getref_desc (const mcpc_prmptarg_t * prmptarg, const char8_t ** ret, size_t *ret_len);
+
+mcpc_errcode_t mcpc_prmptarg_get_data_type (const mcpc_prmptarg_t * prmptarg, mcpc_anytype_t * ret);
+
+mcpc_errcode_t mcpc_prmptarg_set_required (mcpc_prmptarg_t * prmptarg, bool fl);
+
+mcpc_errcode_t mcpc_prmptarg_set_nament (mcpc_prmptarg_t * prmptarg, const char8_t * name);
+
+mcpc_errcode_t mcpc_prmptarg_set_u8str (mcpc_prmptarg_t * prmptarg, const char8_t * arr, size_t arr_len);
+
+// ------------------------------ prmpt ------------------------------
+
+mcpc_errcode_t mcpc_prmpt_getref_name (const mcpc_prmpt_t * prmpt, const char8_t ** ret, size_t *ret_len);
+
+mcpc_errcode_t mcpc_prmpt_getref_desc (const mcpc_prmpt_t * prmpt, const char8_t ** ret, size_t *ret_len);
+
+void mcpc_prmpt_free (mcpc_prmpt_t * prmpt);
+
+// ---------------------------- prmptpool ----------------------------
+
+mcpc_prmptpool_t *mcpc_prmptpool_new ();
+
+void mcpc_prmptpool_free (mcpc_prmptpool_t * prmptpool);
+
+void mcpc_prmptpool_addcpy (mcpc_prmptpool_t * prmptpool, const mcpc_prmpt_t * prmpt);
+
+void mcpc_prmptpool_addfre (mcpc_prmptpool_t * prmptpool, mcpc_prmpt_t * prmpt);
+
+size_t mcpc_prmptpool_get_len (const mcpc_prmptpool_t * prmptpool);
+
+const mcpc_prmpt_t *mcpc_prmptpool_get (const mcpc_prmptpool_t * prmptpool, size_t idx);
+
+const mcpc_prmpt_t *mcpc_prmptpool_getbyname (const mcpc_prmptpool_t * prmptpool, const char8_t * name,
+ size_t name_len);
+
+// -------------------------- prmptarghint --------------------------
+
+const mcpc_prmptarghint_t *mcpc_prmptarg_get_next_prmptarghint (const mcpc_prmptarg_t * prmptarg);
+
+mcpc_errcode_t
+mcpc_prmptarghint_getref_u8str (const mcpc_prmptarg_t * prmptarghint, const char8_t ** ret, size_t *ret_len);
+
+#endif
diff --git a/third_party/mcpc/src/retbuf.c b/third_party/mcpc/src/retbuf.c
new file mode 100644
index 0000000..0429f15
--- /dev/null
+++ b/third_party/mcpc/src/retbuf.c
@@ -0,0 +1,119 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include "retbuf.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mjson.h"
+
+retbuf_t
+retbuf_new (size_t cap, size_t *len)
+{
+ retbuf_t x = {
+ .cap = cap,.buf0 = malloc (cap),.len = len
+ };
+ return x;
+}
+
+void
+retbuf_free (retbuf_t rtbf)
+{
+ free (rtbf.buf0);
+}
+
+uint8_t
+rtbuf_printf_adv (retbuf_t rtbf, size_t advl, const char8_t *u8str, ...)
+{
+ va_list args;
+ int printed;
+ size_t remaining;
+ (void) advl;
+ if (rtbf.buf0 == nullptr || rtbf.len == nullptr || *rtbf.len >= rtbf.cap)
+ return 1;
+ va_start (args, u8str);
+ remaining = rtbf.cap - *rtbf.len;
+ printed = vsnprintf (rtbf.buf0 + *rtbf.len, remaining, (const char *) u8str, args);
+ va_end (args);
+ if (printed < 0 || (size_t) printed >= remaining)
+ {
+ *rtbf.len = rtbf.cap;
+ return 1;
+ }
+ *rtbf.len += (size_t) printed;
+ return 0;
+}
+
+uint8_t
+rtbuf_printf (retbuf_t rtbf, const char8_t *u8str, ...)
+{
+ va_list args;
+ int printed;
+ size_t remaining;
+ if (rtbf.buf0 == nullptr || rtbf.len == nullptr || *rtbf.len >= rtbf.cap)
+ return 1;
+ va_start (args, u8str);
+ remaining = rtbf.cap - *rtbf.len;
+ printed = vsnprintf (rtbf.buf0 + *rtbf.len, remaining, (const char *) u8str, args);
+ va_end (args);
+ if (printed < 0 || (size_t) printed >= remaining)
+ {
+ *rtbf.len = rtbf.cap;
+ return 1;
+ }
+ *rtbf.len += (size_t) printed;
+ return 0;
+}
+
+uint8_t
+rtbuf_printf_mjfmt (retbuf_t rtbf, const char8_t *fmt8, ...)
+{
+ va_list args;
+ va_start (args, fmt8);
+
+ const char *fmt = (const char *) fmt8;
+ struct mjson_fixedbuf fb = { rtbf.buf0, (int)rtbf.cap, (int)*rtbf.len };
+ int advl = mjson_vprintf (&mjson_print_fixed_buf, &fb, fmt, &args);
+
+ if (advl < 0 || (size_t) advl > rtbf.cap - *rtbf.len)
+ *rtbf.len = rtbf.cap;
+ else
+ *rtbf.len += (size_t) advl;
+
+ va_end (args);
+ return 0;
+}
+
+// TODO change _printf_ use this one
+u8_t
+rtbuf_vprintf_mjfmt (retbuf_t rtbf, const char8_t *fmt8, va_list *vargs)
+{
+ const char *fmt = (const char *) fmt8;
+ struct mjson_fixedbuf fb = { rtbf.buf0, (int)rtbf.cap, (int)*rtbf.len };
+ int advl = mjson_vprintf (&mjson_print_fixed_buf, &fb, fmt, vargs); // TODO: __va_list_tag (*)[1]’
+ if (advl < 0 || (size_t) advl > rtbf.cap - *rtbf.len)
+ *rtbf.len = rtbf.cap;
+ else
+ *rtbf.len += (size_t) advl;
+ return 0;
+}
diff --git a/third_party/mcpc/src/retbuf.h b/third_party/mcpc/src/retbuf.h
new file mode 100644
index 0000000..eadcce4
--- /dev/null
+++ b/third_party/mcpc/src/retbuf.h
@@ -0,0 +1,50 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h_retbuf
+#define h_retbuf
+
+#include <stdarg.h>
+
+#include <mcpc/_c23_uchar.h>
+
+#include "ext_stdint.h"
+
+
+typedef struct retbuf
+{
+ const size_t cap;
+ char *const buf0;
+ size_t *len;
+} retbuf_t;
+
+retbuf_t retbuf_new (size_t cap, size_t *len);
+
+void retbuf_free (retbuf_t rtbf);
+
+u8_t rtbuf_printf_adv (retbuf_t rtbf, size_t advl, const char8_t * u8str, ...);
+
+u8_t rtbuf_printf (retbuf_t rtbf, const char8_t * u8str, ...);
+
+u8_t rtbuf_printf_mjfmt (retbuf_t rtbf, const char8_t * u8str, ...);
+
+
+#endif
diff --git a/third_party/mcpc/src/rsc.c b/third_party/mcpc/src/rsc.c
new file mode 100644
index 0000000..be5627b
--- /dev/null
+++ b/third_party/mcpc/src/rsc.c
@@ -0,0 +1,177 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include "rsc.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "ext_string.h"
+
+#include "errcode.h"
+#include "alloc.h"
+#include "anydata.h"
+
+MCPC_API mcpc_rsc_t *
+mcpc_rsc_new (const char8_t *uri, const size_t uri_len, const char8_t *name, const size_t name_len)
+{
+ mcpc_anydata_t *anydata = mcpc_anydata_new_typ_meta1 (MCPC_U8STR, name, name_len);
+
+ anydata->flags |= ANYDATA_FL_RSC;
+ anydata->data_mime = MCPC_MIME_TEXT_PLAIN;
+
+ if (MCPC_EC_0 != mcpc_anydata_set_u8str (anydata, uri, uri_len))
+ goto badret;
+
+ return (mcpc_rsc_t *) anydata;
+
+badret:
+ mcpc_anydata_free (anydata);
+ return nullptr;
+}
+
+MCPC_API mcpc_rsc_t *
+mcpc_rsc_new2 (const char8_t *uri_nt, const char8_t *nament)
+{
+ return mcpc_rsc_new (uri_nt, u8strlen (uri_nt), nament, u8strlen (nament));
+}
+
+MCPC_API mcpc_rsc_t *
+mcpc_rsc_new3 (mcpc_mime_t mime, const char8_t *uri_nt, const char8_t *nament)
+{
+ mcpc_rsc_t *rsc = mcpc_rsc_new2 (uri_nt, nament);
+ rsc->data_mime = mime;
+ return rsc;
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_rsc_get_uri (const mcpc_rsc_t *rsc, char8_t *ret, size_t ret_cap, size_t *ret_len)
+{
+ return mcpc_anydata_get_u8str (rsc, ret, ret_cap, ret_len);
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_rsc_getref_uri (const mcpc_rsc_t *rsc, const char8_t **ret, size_t *ret_len)
+{
+ return mcpc_anydata_getref_u8str (rsc, ret, ret_len);
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_rsc_getref_name (const mcpc_rsc_t *rsc, const char8_t **ret, size_t *ret_len)
+{
+ return mcpc_anydata_getref_meta1 (rsc, ret, ret_len);
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_rsc_get_mime (const mcpc_rsc_t *rsc, mcpc_mime_t *ret)
+{
+ bug_if_nullptr (rsc);
+ bug_if_nullptr (ret);
+ *ret = rsc->data_mime;
+ return MCPC_EC_0;
+}
+
+MCPC_API void
+mcpc_rsc_set_mime (mcpc_rsc_t *rsc, mcpc_mime_t mime)
+{
+ bug_if_nullptr (rsc);
+ rsc->data_mime = mime;
+}
+
+MCPC_API void
+mcpc_rsc_free (mcpc_rsc_t *rsc)
+{
+ mcpc_anydata_free (rsc);
+}
+
+/* = = = = = = = = = = = = = = = = rscpool = = = = = = = = = = = = = = = = */
+
+mcpc_rscpool_t *
+mcpc_rscpool_new ()
+{
+ return mcpc_anypool_new ();
+}
+
+void
+mcpc_rscpool_free (mcpc_rscpool_t *rscpool)
+{
+ mcpc_anypool_free (rscpool);
+}
+
+void
+mcpc_rscpool_addcpy (mcpc_rscpool_t *rscpool, const mcpc_rsc_t *rsc)
+{
+ mcpc_anypool_addcpy (rscpool, rsc);
+}
+
+void
+mcpc_rscpool_addfre (mcpc_rscpool_t *rscpool, mcpc_rsc_t *rsc)
+{
+ mcpc_anypool_addfre (rscpool, rsc);
+}
+
+size_t
+mcpc_rscpool_get_len (const mcpc_rscpool_t *rscpool)
+{
+ bug_if_nullptr (rscpool);
+ size_t ret = 0;
+ ret = ((const mcpc_anypool_t *) rscpool)->len;
+ return ret;
+}
+
+const mcpc_rsc_t *
+mcpc_rscpool_get (const mcpc_rscpool_t *rscpool, size_t idx)
+{
+ return mcpc_anypool_get (rscpool, idx);
+}
+
+const mcpc_rsc_t *
+mcpc_rscpool_getbyname (const mcpc_rscpool_t *rscpool, const char8_t *name, size_t name_len)
+{
+ return mcpc_anypool_getbymeta1 (rscpool, name, name_len);
+}
+
+const mcpc_rsc_t *
+mcpc_rscpool_get_next (const mcpc_rscpool_t *rscpool)
+{
+ bug_if_nullptr (rscpool);
+
+ static size_t idx = 0;
+
+ const mcpc_anypool_t *pool = rscpool;
+ if (pool == nullptr)
+ return nullptr;
+
+ const size_t pool_len = pool->len;
+ if (pool_len == 0)
+ return nullptr;
+
+ if (idx == pool_len) // TODO MT-SAFE
+ {
+ idx = 0;
+ return nullptr;
+ }
+
+ const mcpc_anydata_t *ret = pool->head + idx;
+ idx += 1;
+
+ return ret;
+}
diff --git a/third_party/mcpc/src/rsc.h b/third_party/mcpc/src/rsc.h
new file mode 100644
index 0000000..d9f1477
--- /dev/null
+++ b/third_party/mcpc/src/rsc.h
@@ -0,0 +1,51 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h_rsc
+#define h_rsc
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include <mcpc/_c23_keywords.h>
+#include <mcpc/_c23_uchar.h>
+
+#include <mcpc/rsc.h>
+
+typedef mcpc_anypool_t mcpc_rscpool_t;
+
+// ---------------------------- rscpool ----------------------------
+
+mcpc_rscpool_t *mcpc_rscpool_new ();
+
+void mcpc_rscpool_free (mcpc_rscpool_t * rscpool);
+
+void mcpc_rscpool_add_cp (mcpc_rscpool_t * rscpool, const mcpc_rsc_t * tmp_rsc);
+
+void mcpc_rscpool_addfre (mcpc_rscpool_t * rscpool, mcpc_rsc_t * tmp_rsc);
+
+const mcpc_rsc_t *mcpc_rscpool_get_const (const mcpc_rscpool_t * rscpool, size_t idx);
+
+const mcpc_rsc_t *mcpc_rscpool_get_next (const mcpc_rscpool_t * rscpool);
+
+const mcpc_rsc_t *mcpc_rscpool_getbyname (const mcpc_rscpool_t * rscpool, const char8_t * name, size_t name_len);
+
+#endif
diff --git a/third_party/mcpc/src/serlz.c b/third_party/mcpc/src/serlz.c
new file mode 100644
index 0000000..0721233
--- /dev/null
+++ b/third_party/mcpc/src/serlz.c
@@ -0,0 +1,619 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#define __TU__ "serlz"
+
+#include "serlz.h"
+
+#include "ext_string.h"
+#include "log.h"
+
+
+/* = = = = = = = = = = = = = = = = = tool = = = = = = = = = = = = = = = = = */
+
+u8_t
+serlz_tool_inputschema_properties_prop (retbuf_t rtbf, const mcpc_toolprop_t *tprop)
+{
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+
+ const char8_t *name, *desc = nullptr;
+ size_t name_len, desc_len = 0;
+
+ bug_if_nz (mcpc_toolprop_getref_name (tprop, &name, &name_len));
+ bug_if_nz (mcpc_toolprop_getref_desc (tprop, &desc, &desc_len));
+
+ mcpc_anytype_t typ = MCPC_NONE;
+ bug_if_nz (mcpc_toolprop_get_data_type (tprop, &typ));
+
+ if (false)
+ ;
+ else if (mcpc_anytype_is_intlike (typ))
+ {
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%Q"), "type", "integer");
+ }
+ else if (typ == MCPC_U8STR)
+ {
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%Q"), "type", "string");
+ }
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%.*Q"), "title", name_len, name);
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%.*Q"), "description", desc_len, desc);
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+ return 0;
+}
+
+u8_t
+serlz_tool_inputschema_properties (retbuf_t rtbf, const mcpc_toolproppool_t *tppool)
+{
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+
+ if (tppool != nullptr)
+ {
+ size_t len_tppool = mcpc_toolproppool_get_len (tppool);
+ if (len_tppool > 0)
+ {
+ for (size_t i = 0; i < len_tppool; i++)
+ {
+ const mcpc_toolprop_t *tprop = mcpc_toolproppool_get (tppool, i);
+ size_t name_len = 0;
+ const char8_t *name = nullptr;
+ bug_if (mcpc_toolprop_getref_name (tprop, &name, &name_len));
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%.*Q:"), name_len, name);
+ serlz_tool_inputschema_properties_prop (rtbf, tprop);
+ if (i != len_tppool - 1)
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ }
+ }
+ }
+
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+ return 0;
+}
+
+u8_t
+serlz_tool_inputschema (retbuf_t rtbf, const mcpc_tool_t *tool)
+{
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%Q"), "type", "object");
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "properties");
+ serlz_tool_inputschema_properties (rtbf, mcpc_tool_get_toolproppool (tool));
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:["), "required");
+ bool first = true;
+ size_t property_count = mcpc_tool_get_toolprop_len (tool);
+ for (size_t i = 0; i < property_count; ++i)
+ {
+ const mcpc_toolprop_t *property = mcpc_tool_get_toolprop (tool, i);
+ const char8_t *name = nullptr;
+ size_t name_len = 0;
+ if (!mcpc_toolprop_is_required (property))
+ continue;
+ bug_if_nz (mcpc_toolprop_getref_name (property, &name, &name_len));
+ if (!first)
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%.*Q"), name_len, name);
+ first = false;
+ }
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("]"));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+ return 0;
+}
+
+uint8_t
+serlz_tool (retbuf_t rtbf, const mcpc_tool_t *tool)
+{
+ const char8_t *name, *desc = nullptr;
+ size_t name_len, desc_len = 0;
+ bug_if_nz (mcpc_toolprop_getref_name (tool, &name, &name_len));
+ bug_if_nz (mcpc_toolprop_getref_desc (tool, &desc, &desc_len));
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%.*Q"), "name", name_len, name);
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%.*Q"), "description", desc_len, desc);
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "inputSchema");
+ serlz_tool_inputschema (rtbf, tool);
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+ return 0;
+}
+
+u8_t
+serlz_toolpool (retbuf_t rtbf, const mcpc_toolpool_t *toolpool)
+{
+ size_t len_tpool = mcpc_toolpool_get_len (toolpool);
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "tools");
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("["));
+
+ // TODO TODO TODO use iterator
+ if (toolpool != nullptr && len_tpool > 0)
+ {
+ for (size_t i = 0; i < len_tpool; i++)
+ {
+ const mcpc_tool_t *cur_tool = mcpc_toolpool_get (toolpool, i);
+ serlz_tool (rtbf, cur_tool);
+ if (i != len_tpool - 1)
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ }
+ }
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("]"));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+ return 0;
+}
+
+/* = = = = = = = = = = = = = = = = = prmpt = = = = = = = = = = = = = = = = = */
+
+u8_t
+serlz_prmptarg (retbuf_t rtbf, const mcpc_prmptarg_t *prmptarg)
+{
+ const char8_t *name, *desc = nullptr;
+ size_t name_len, desc_len = 0;
+ bool required = false;
+
+ bug_if_nz (mcpc_prmptarg_getref_name (prmptarg, &name, &name_len));
+ bug_if_nz (mcpc_prmptarg_getref_desc (prmptarg, &desc, &desc_len));
+ required = mcpc_prmptarg_is_required (prmptarg);
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%.*Q"), "name", name_len, name);
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%.*Q"), "description", desc_len, desc);
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%B"), "required", required);
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+ return 0;
+}
+
+uint8_t
+serlz_prmpt (retbuf_t rtbf, const mcpc_prmpt_t *prmpt)
+{
+ const char8_t *name, *desc = nullptr;
+ size_t name_len, desc_len = 0;
+ bug_if_nz (mcpc_prmptarg_getref_name (prmpt, &name, &name_len));
+ bug_if_nz (mcpc_prmptarg_getref_desc (prmpt, &desc, &desc_len));
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%.*Q"), "name", name_len, name);
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%.*Q"), "description", desc_len, desc);
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "arguments");
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("["));
+
+ const mcpc_prmptarg_t *cur_prmptarg = nullptr;
+ bool first = true;
+ while ((cur_prmptarg = mcpc_prmpt_get_next_prmptarg (prmpt)))
+ {
+ if (!first)
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ serlz_prmptarg (rtbf, cur_prmptarg);
+ first = false;
+ }
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("]"));
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+ return 0;
+}
+
+u8_t
+serlz_prmptpool (retbuf_t rtbf, const mcpc_prmptpool_t *prmptpool)
+{
+ size_t len_tpool = mcpc_prmptpool_get_len (prmptpool);
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "prompts");
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("["));
+
+ if (prmptpool != nullptr && len_tpool > 0)
+ {
+ for (size_t i = 0; i < len_tpool; i++)
+ {
+ const mcpc_prmpt_t *cur_prmpt = mcpc_prmptpool_get (prmptpool, i);
+ serlz_prmpt (rtbf, cur_prmpt);
+ if (i != len_tpool - 1)
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ }
+ }
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("]"));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+ return 0;
+}
+
+/* = = = = = = = = = = = = = = = = = = rsc = = = = = = = = = = = = = = = = = = */
+
+u8_t
+serlz_rsc (retbuf_t rtbf, const mcpc_rsc_t *rsc)
+{
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+
+ const char8_t *ref_uri = nullptr;
+ size_t ref_uri_len = 0;
+ const char8_t *ref_name = nullptr;
+ size_t ref_name_len = 0;
+ mcpc_mime_t mime = MCPC_MIME_NONE;
+
+ mcpc_rsc_getref_uri (rsc, &ref_uri, &ref_uri_len);
+ mcpc_rsc_getref_name (rsc, &ref_name, &ref_name_len);
+ mcpc_rsc_get_mime (rsc, &mime);
+
+ bool serlz_uri = ref_uri_len > 0 && ref_uri != nullptr;
+ bool serlz_name = ref_name_len > 0 && ref_name != nullptr;
+ bool serlz_mime = mime != MCPC_MIME_NONE;
+
+ // uri is ID, hence first
+ if (serlz_uri)
+ {
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%.*Q"), "uri", ref_uri_len, (const char *) ref_uri);
+ }
+
+ if (serlz_name)
+ {
+ if (serlz_uri)
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%.*Q"), "name", ref_name_len, ref_name);
+ }
+
+ if (serlz_mime)
+ {
+ if (serlz_name || serlz_uri)
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "mimeType");
+ if (false);
+ else if (mime == MCPC_MIME_TEXT_PLAIN)
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q"), u8c1 ("text/plain"));
+ else if (mime == MCPC_MIME_IMAGE_PNG)
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q"), u8c1 ("image/png"));
+ else if (mime == MCPC_MIME_IMAGE_JPEG)
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q"), u8c1 ("image/jpeg"));
+ else if (mime == MCPC_MIME_IMAGE_GIF)
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q"), u8c1 ("image/gif"));
+ else if (mime == MCPC_MIME_AUDIO_MPEG)
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q"), u8c1 ("audio/mpeg"));
+ else if (mime == MCPC_MIME_AUDIO_WAV)
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q"), u8c1 ("audio/wav"));
+ else if (mime == MCPC_MIME_VIDEO_MPEG)
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q"), u8c1 ("video/mpeg"));
+ else if (mime == MCPC_MIME_VIDEO_AVI)
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q"), u8c1 ("video/avi"));
+ else if (mime == MCPC_MIME_APPLI_OCTS)
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q"), u8c1 ("application/octet-stream"));
+ else if (mime == MCPC_MIME_APPLI_JSON)
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q"), u8c1 ("application/json"));
+ else if (mime == MCPC_MIME_APPLI_PDF)
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q"), u8c1 ("application/pdf"));
+ }
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+
+ return 0;
+}
+
+u8_t
+serlz_rscpool (retbuf_t rtbf, const mcpc_rscpool_t *rscpool)
+{
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "resources");
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("["));
+
+
+ const mcpc_rsc_t *cur_rsc = nullptr;
+ bool first = true;
+ while ((cur_rsc = mcpc_rscpool_get_next (rscpool)))
+ {
+ if (!first)
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ serlz_rsc (rtbf, cur_rsc);
+ first = false;
+ }
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("]"));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+ return 0;
+}
+
+/* = = = = = = = = = = = = = = = = = = = - = = = = = = = = = = = = = = = = = = = */
+
+uint8_t
+serlz_serverInfo (retbuf_t rtbf, const mcpc_server_t *sv)
+{
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%.*Q"), "name", sv->name_len, sv->name);
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%.*Q"), "version", sv->ver_len, sv->ver);
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+ return 0;
+}
+
+uint8_t
+serlz_capasprompts (retbuf_t rtbf, const mcpc_server_t *sv)
+{
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "listChanged");
+ bool supp_lschg = mcpc_server_supp_prompts_lschg (sv);
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%B"), supp_lschg);
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+ return 0;
+}
+
+uint8_t
+serlz_capastools (retbuf_t rtbf, const mcpc_server_t *sv)
+{
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "listChanged");
+ bool supp_lschg = mcpc_server_supp_tools_lschg (sv);
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%B"), supp_lschg);
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+ return 0;
+}
+
+uint8_t
+serlz_capasresources (retbuf_t rtbf, const mcpc_server_t *sv)
+{
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "subscribe");
+ bool supp_subs = mcpc_server_supp_rscs_subs (sv);
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%B"), supp_subs);
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "listChanged");
+ bool supp_lschg = mcpc_server_supp_rscs_lschg (sv);
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%B"), supp_lschg);
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+ return 0;
+}
+
+uint8_t
+serlz_capas (retbuf_t rtbf, const mcpc_server_t *sv)
+{
+ bool need_comma = false;
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+
+ if (mcpc_server_supp_prompts (sv))
+ {
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "prompts");
+ serlz_capasprompts (rtbf, sv);
+ need_comma = true;
+ }
+
+ if (mcpc_server_supp_tools (sv))
+ {
+ if (need_comma)
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "tools");
+ serlz_capastools (rtbf, sv);
+ need_comma = true;
+ }
+
+ if (mcpc_server_supp_rscs (sv))
+ {
+ if (need_comma)
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "resources");
+ serlz_capasresources (rtbf, sv);
+ }
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+ return 0;
+}
+
+uint8_t
+serlz_initres (retbuf_t rtbf, const mcpc_server_t *sv)
+{
+ rtbuf_printf (rtbf, u8c1 ("{"));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%Q"), "protocolVersion", "2024-11-05");
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "serverInfo");
+ serlz_serverInfo (rtbf, sv);
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "capabilities");
+ serlz_capas (rtbf, sv);
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+ return 0;
+}
+
+/* = = = = = = = = = = = = = = = = = ucbr = = = = = = = = = = = = = = = = = */
+
+uint8_t
+serlz_ucbrchd_tool_callres (retbuf_t rtbf, const mcpc_ucbrchd_t *ucbrchd)
+{
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%Q"), "type", "text");
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "text");
+
+ mcpc_anytype_t data_type = MCPC_NONE;
+ bug_if_nz (mcpc_ucbrchd_get_data_type (ucbrchd, &data_type));
+
+ if (false)
+ ;
+ else if (data_type == MCPC_U8STR)
+ {
+ const char8_t *s = nullptr;
+ size_t s_len = 0;
+ mcpc_ucbrchd_getref_u8str (ucbrchd, &s, &s_len);
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%.*Q"), s_len, (const char *) s);
+ }
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+ return 0;
+}
+
+uint8_t
+serlz_ucbrchd_prmpt_callres (retbuf_t rtbf, const mcpc_ucbrchd_t *ucbrchd)
+{
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "role");
+ if (mcpc_ucbrchd_is_assist_prmpt (ucbrchd))
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q"), "assistant");
+ else
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q"), "user");
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "content");
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%Q"), "type", "text");
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "text");
+
+ mcpc_anytype_t data_type = MCPC_NONE;
+ bug_if_nz (mcpc_ucbrchd_get_data_type (ucbrchd, &data_type));
+
+ if (false)
+ ;
+ else if (data_type == MCPC_U8STR)
+ {
+ const char8_t *s = nullptr;
+ size_t s_len = 0;
+ mcpc_ucbrchd_getref_u8str (ucbrchd, &s, &s_len);
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%.*Q"), s_len, (const char *) s);
+ }
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+ return 0;
+}
+
+uint8_t
+serlz_ucbrchd (retbuf_t rtbf, const mcpc_ucbrchd_t *ucbrchd)
+{
+ if (false);
+ else if (mcpc_ucbrchd_is_tool_callres (ucbrchd))
+ {
+ return serlz_ucbrchd_tool_callres (rtbf, ucbrchd);
+ }
+ else if (mcpc_ucbrchd_is_prmpt_callres (ucbrchd))
+ {
+ return serlz_ucbrchd_prmpt_callres (rtbf, ucbrchd);
+ }
+ return 0;
+}
+
+u8_t
+serlz_ucbr (retbuf_t rtbf, const mcpc_ucbr_t *ucbr)
+{
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("["));
+
+ size_t length = mcpc_ucbr_get_len (ucbr);
+ for (size_t i = 0; i < length; ++i)
+ {
+ const mcpc_ucbrchd_t *cur_ucbrchd = mcpc_ucbr_get (ucbr, i);
+ if (i != 0)
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ serlz_ucbrchd (rtbf, cur_ucbrchd);
+ }
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("]"));
+ return 0;
+}
+
+u8_t
+serlz_tool_callres (retbuf_t rtbf, const mcpc_ucbr_t *ucbr)
+{
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+
+ if (mcpc_ucbr_is_err (ucbr))
+ {
+ // https://modelcontextprotocol.io/docs/concepts/tools#error-handling-2
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%B"), "isError", true);
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ }
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "content");
+ serlz_ucbr (rtbf, ucbr);
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+ return 0;
+}
+
+u8_t
+serlz_prmpt_callres (retbuf_t rtbf, const mcpc_ucbr_t *ucbr)
+{
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%Q"), "description", "Prompt List");
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "messages");
+ serlz_ucbr (rtbf, ucbr);
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+ return 0;
+}
+
+/* = = = = = = = = = = = = = = = = complt = = = = = = = = = = = = = = = = */
+
+u8_t
+serlz_complt (retbuf_t rtbf, const mcpc_complt_t *complt)
+{
+ size_t total = 0;
+
+ mcpc_complt_get_compltcandi_len (complt, &total);
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "completion");
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("{"));
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%lu"), "total", total);
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:%B"), "hasMore", false);
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%Q:"), "values");
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("["));
+
+ const mcpc_compltcand_t *cur_compltcand = nullptr;
+ bool first = true;
+ while ((cur_compltcand = mcpc_complt_get_next_compltcand (complt)))
+ {
+ if (!first)
+ rtbuf_printf_mjfmt (rtbf, u8c1 (","));
+ const char8_t *sbuf = nullptr;
+ size_t sbuf_len = 0;
+ bug_if_nz (mcpc_compltcand_getref_u8str (cur_compltcand, &sbuf, &sbuf_len));
+ bug_if_nullptr (sbuf);
+ bug_if_zero (sbuf_len);
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("%.*Q"), sbuf_len, sbuf);
+ first = false;
+ }
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("]"));
+
+
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+ rtbuf_printf_mjfmt (rtbf, u8c1 ("}"));
+ return 0;
+}
diff --git a/third_party/mcpc/src/serlz.h b/third_party/mcpc/src/serlz.h
new file mode 100644
index 0000000..9ee6618
--- /dev/null
+++ b/third_party/mcpc/src/serlz.h
@@ -0,0 +1,62 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h_serialize
+#define h_serialize
+
+#include "retbuf.h"
+#include "tool.h"
+#include "rsc.h"
+#include "server.h"
+#include "ucbr.h"
+#include "complt.h"
+
+#define L_QUO u8c1("\"")
+#define R_QUO u8c1("\"")
+#define L_QUO_D u8c1("(")
+#define R_QUO_D u8c1(")")
+
+u8_t serlz_tool (retbuf_t rtbf, const mcpc_tool_t * tool);
+
+u8_t serlz_toolpool (retbuf_t rtbf, const mcpc_toolpool_t * pool);
+
+u8_t serlz_prmptpool (retbuf_t rtbf, const mcpc_prmptpool_t * prmptpool);
+
+u8_t serlz_initres (retbuf_t rtbf, const mcpc_server_t * sv);
+
+u8_t serlz_rsc (retbuf_t rtbf, const mcpc_rsc_t * rsc);
+
+u8_t serlz_rscpool (retbuf_t rtbf, const mcpc_rscpool_t * rscpool);
+
+// deprecated
+u8_t serlz_ucbrchd (retbuf_t rtbf, const mcpc_ucbrchd_t * ucbrchd);
+
+// deprecated
+u8_t serlz_ucbr (retbuf_t rtbf, const mcpc_ucbr_t * ucbr);
+
+u8_t serlz_tool_callres (retbuf_t rtbf, const mcpc_ucbr_t * ucbr);
+
+u8_t serlz_prmpt_callres (retbuf_t rtbf, const mcpc_ucbr_t * ucbr);
+
+u8_t serlz_complt (retbuf_t rtbf, const mcpc_complt_t * complt);
+
+
+#endif
diff --git a/third_party/mcpc/src/server.c b/third_party/mcpc/src/server.c
new file mode 100644
index 0000000..07f8e8b
--- /dev/null
+++ b/third_party/mcpc/src/server.c
@@ -0,0 +1,1830 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#define __TU__ "server"
+
+#include "server.h"
+
+#include "ext_string.h"
+
+#include "alloc.h"
+#include "anydata.h"
+#include <mcpc/anydata.h>
+#include "errcode.h"
+#include "log.h"
+#include "retbuf.h"
+#include "rsc.h"
+#include "tool.h"
+#include "ucbr.h"
+#include "serlz.h"
+
+#include <limits.h>
+#include <math.h>
+
+#define BUFCAP_INIT_JSONREQ 1024
+#define CH_SPC 0x20
+#define CH_LF 0x0a
+#define CH_LBRC 0x7b
+#define CH_RBRC 0x7d
+#define CH_QUO 0x22
+#define CH_BSLASH 0x5c
+
+
+#if is_unix
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#elif is_win
+#include <winsock2.h>
+typedef i32_t socklen_t;
+#endif
+
+struct mjson_fixedbuf
+_new_mjson_fixedbuf ()
+{
+ struct mjson_fixedbuf fbuf = { nullptr, 0, 0 };
+ char *buf = mcpc_alloc (65536);
+ fbuf.ptr = buf;
+ fbuf.size = 65536;
+ fbuf.len = 0;
+ return fbuf;
+}
+
+bool
+ensure_buf_suffi (struct mjson_fixedbuf *fbuf, size_t ipt_len)
+{
+ char *grown;
+ if (fbuf == nullptr || ipt_len == 0 || ipt_len > INT_MAX)
+ return false;
+ if (ipt_len <= (size_t) fbuf->size)
+ {
+ return true;
+ }
+
+ grown = mcpc_realloc (fbuf->ptr, ipt_len);
+ if (grown == nullptr)
+ return false;
+ fbuf->ptr = grown;
+ fbuf->size = (int) ipt_len;
+ return true;
+}
+
+void
+_connpool_init (mcpc_connpool_t *ret)
+{
+ mcpc_connpool_t connpool = { 255, 0, nullptr };
+ memcpy (ret, &connpool, sizeof (mcpc_connpool_t));
+}
+
+bool
+_connpool_is_buggy (const mcpc_connpool_t *connpool)
+{
+ if (connpool == nullptr)
+ {
+ return true;
+ }
+ if (connpool->cap == 0)
+ {
+ return true;
+ }
+ if (connpool->len != 0)
+ {
+ u8_t nexist = 0;
+ mcpc_conn_t *cur_conn = connpool->head;
+ while (cur_conn != nullptr)
+ {
+ cur_conn = cur_conn->nex;
+ nexist += 1; // OR
+ // mcpc_assert(!ckd_add(&nexist, nexist, 1), MCPC_EC_BUG);
+ }
+ if (nexist != connpool->len)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+// extract a field's unquoted string
+mcpc_errcode_t
+_tulc_jstr_get_field_u8str (struct jsonrpc_request *r, const char8_t *jstr, size_t jstr_len, const char8_t *jpathnt,
+ char8_t **ret, size_t *ret_len, size_t ret_cap, bool not_empty)
+{
+ const char *jstr2 = (const char *) jstr;
+ int jstr_len2 = (int) jstr_len;
+ const char *jpathnt2 = (const char *) jpathnt;
+ const char8_t *ret_q = nullptr;
+ int32_t ret_q_len = 0;
+ int tmptok;
+
+ tmptok = mjson_find (jstr2, jstr_len2, jpathnt2, (const char **) &ret_q, (int *) &ret_q_len);
+ if (tmptok != MJSON_TOK_STRING || ret_q == nullptr || ret_q_len == 0)
+ goto field_invalid;
+
+ int getres = mjson_get_string ((const char *) ret_q, ret_q_len, "$", (char *) *ret, ret_cap);
+ if (getres == -1)
+ return MCPC_EC_BUFCAP;
+ if (not_empty && getres == 0)
+ goto field_invalid;
+ *ret_len = (size_t) getres;
+
+ return MCPC_EC_0;
+
+field_invalid:
+ jsonrpc_return_error (r, JSONRPC_ERROR_BAD_PARAMS, "field invalid", "%Q", jpathnt2);
+ return MCPC_EC_JSON_FIELD_INVALID;
+}
+
+// extract a field's unquoted string
+mcpc_errcode_t
+_tulc_jparams_get_field_u8str (struct jsonrpc_request *r,
+ const char8_t *jpathnt, char8_t **ret, size_t *ret_len, size_t ret_cap, bool not_empty)
+{
+ return _tulc_jstr_get_field_u8str (r, (const char8_t *) r->params, r->params_len, jpathnt, ret, ret_len,
+ ret_cap, not_empty);
+}
+
+// extract a field's value, that is, everything
+mcpc_errcode_t
+_tulc_jparams_getref_field_obj (struct jsonrpc_request *r, const char8_t *jpathnt, const char8_t **ret, size_t *ret_len)
+{
+ const char *jpathnt2 = (const char *) jpathnt;
+ int tmptok;
+
+ tmptok = mjson_find (r->params, r->params_len, jpathnt2, (const char **) ret, (int *) ret_len);
+ if (tmptok != MJSON_TOK_OBJECT || ret == nullptr || ret_len == 0)
+ goto field_invalid;
+
+ return MCPC_EC_0;
+
+field_invalid:
+ jsonrpc_return_error (r, JSONRPC_ERROR_BAD_PARAMS, "field invalid", "%Q", jpathnt2);
+ return MCPC_EC_JSON_FIELD_INVALID;
+}
+
+
+// extract a field's value, that is, everything
+mcpc_errcode_t
+_tulc_jparams_getref_field_u8str_q (struct jsonrpc_request *r, const char8_t *jpathnt, const char8_t **ret,
+ size_t *ret_len)
+{
+ const char *jpathnt2 = (const char *) jpathnt;
+ int tmptok;
+
+ tmptok = mjson_find (r->params, r->params_len, jpathnt2, (const char **) ret, (int *) ret_len);
+ if (tmptok != MJSON_TOK_STRING || ret == nullptr || ret_len == 0)
+ goto field_invalid;
+
+ return MCPC_EC_0;
+
+field_invalid:
+ jsonrpc_return_error (r, JSONRPC_ERROR_BAD_PARAMS, "field invalid", "%Q", jpathnt2);
+ return MCPC_EC_JSON_FIELD_INVALID;
+}
+
+
+// extract a field's value, that is, everything
+mcpc_errcode_t
+_tulc_jstr_get_field_number (struct jsonrpc_request *r, const char8_t *jstr, size_t jstr_len,
+ const char8_t *jpathnt, double *ret)
+{
+ const char *jpathnt2 = (const char *) jpathnt;
+ double numval = 0;
+
+ int ngot = mjson_get_number ((const char *) jstr, jstr_len, jpathnt2, &numval);
+
+ if (ngot != 1)
+ goto field_invalid;
+
+ *ret = numval;
+
+ return MCPC_EC_0;
+
+field_invalid:
+ jsonrpc_return_error (r, JSONRPC_ERROR_BAD_PARAMS, "field invalid", "%Q", jpathnt2);
+ return MCPC_EC_JSON_FIELD_INVALID;
+}
+
+
+mcpc_errcode_t
+mcpc_conn_free (mcpc_conn_t *conn)
+{
+ bug_if_nullptr (conn);
+
+ if (conn->cname != nullptr)
+ {
+ free ((void *) conn->cname);
+ conn->cname = nullptr;
+ conn->cname_len = 0;
+ }
+
+ if (conn->nex != nullptr)
+ {
+ mcpc_conn_free (conn->nex);
+ }
+
+ free (conn);
+
+ return MCPC_EC_0;
+}
+
+mcpc_errcode_t
+mcpc_connpool_free (mcpc_connpool_t *connpool)
+{
+ bug_if_nullptr (connpool);
+ mcpc_conn_free (connpool->head);
+ free (connpool);
+ return MCPC_EC_0;
+}
+
+MCPC_API mcpc_server_t *
+mcpc_server_new (mcpc_server_type_t typ, mcpc_toolpool_t *toolpool, u32_t vabeg, ...)
+{
+ mcpc_assert (typ != MCPC_SV_NONE, MCPC_EC_BUG);
+ mcpc_assert (toolpool != nullptr, MCPC_EC_BUG);
+
+ mcpc_server_t *sv = (mcpc_server_t *) mcpc_alloc (sizeof (mcpc_server_t));
+
+ sv->typ = typ;
+ sv->toolpool = toolpool;
+ sv->prmptpool = mcpc_prmptpool_new ();
+ sv->rscpool = mcpc_rscpool_new ();
+ sv->rpcres = _new_mjson_fixedbuf ();
+ sv->connpool = mcpc_alloc (sizeof (mcpc_connpool_t));
+ _connpool_init ((mcpc_connpool_t *) sv->connpool);
+
+ if (false)
+ ;
+ else if (typ == MCPC_SV_IOSTRM)
+ {
+ va_list ap;
+ va_start (ap, vabeg);
+ FILE *strm0 = va_arg (ap, FILE *);
+ mcpc_assert (strm0 != nullptr, MCPC_EC_BUG);
+ FILE *strm1 = va_arg (ap, FILE *);
+ mcpc_assert (strm1 != nullptr, MCPC_EC_BUG);
+ mcpc_conn_t *conn0 = mcpc_conn_new (MCPC_CONN_STRM, strm0);
+ mcpc_assert (MCPC_EC_0 == mcpc_connpool_addref ((mcpc_connpool_t *) sv->connpool, conn0), MCPC_EC_BUG);
+ mcpc_conn_t *conn1 = mcpc_conn_new (MCPC_CONN_STRM, strm1);
+ mcpc_assert (MCPC_EC_0 == mcpc_connpool_addref ((mcpc_connpool_t *) (sv->connpool), conn1), MCPC_EC_BUG);
+ va_end (ap);
+ }
+ else if (typ == MCPC_SV_TCP)
+ {
+ }
+ else
+ {
+ mcpc_assert (false, MCPC_EC_BUG);
+ }
+
+ ch8_t *default_name = u8c1 ("untitled");
+ u8_t default_name_len = (u8_t) u8strlen (default_name);
+ sv->name = mcpc_alloc ((size_t) default_name_len);
+ memcpy ((void *) sv->name, default_name, default_name_len);
+ sv->name_len = default_name_len;
+
+ ch8_t *default_ver = u8c1 ("0.1");
+ u8_t default_ver_len = (u8_t) u8strlen (default_ver);
+ sv->ver = mcpc_alloc ((size_t) default_ver_len);
+ memcpy ((void *) sv->ver, default_ver, default_ver_len);
+ sv->ver_len = default_ver_len;
+
+ sv->capa = 0;
+
+ return sv;
+}
+
+MCPC_API mcpc_server_t *
+mcpc_server_new_iostrm (const FILE *const strm_in, const FILE *const strm_out)
+{
+ mcpc_assert (strm_in != nullptr, MCPC_EC_BUG);
+ mcpc_assert (strm_out != nullptr, MCPC_EC_BUG);
+
+ mcpc_toolpool_t *toolpool = mcpc_toolpool_new ();
+ mcpc_server_t *sv = mcpc_server_new (MCPC_SV_IOSTRM, toolpool, true, strm_in, strm_out);
+
+ return sv;
+}
+
+MCPC_API mcpc_server_t *
+mcpc_server_new_tcp ()
+{
+ mcpc_toolpool_t *toolpool = mcpc_toolpool_new ();
+ mcpc_server_t *sv = mcpc_server_new (MCPC_SV_TCP, toolpool, false);
+
+ return sv;
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_server_set_name (mcpc_server_t *sv, const char8_t *name, size_t name_len)
+{
+ bug_if (name_len > (size_t) UINT8_MAX);
+ sv->name = mcpc_realloc ((void *) sv->name, name_len);
+ memcpy ((void *) sv->name, name, name_len);
+ sv->name_len = (u8_t) name_len;
+ return MCPC_EC_0;
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_server_set_nament (mcpc_server_t *sv, const char8_t *nament)
+{
+ return mcpc_server_set_name (sv, nament, u8strlen (nament));
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_server_capa_enable_prmt (mcpc_server_t *sv)
+{
+ sv->capa |= MCPC_CAPA_PRMT;
+ return MCPC_EC_0;
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_server_capa_enable_prmt_listchg (mcpc_server_t *sv)
+{
+ sv->capa |= MCPC_CAPA_PRMT_LISTCHG;
+ return MCPC_EC_0;
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_server_capa_enable_tool (mcpc_server_t *sv)
+{
+ sv->capa |= MCPC_CAPA_TOOL;
+ return MCPC_EC_0;
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_server_capa_enable_tool_listchg (mcpc_server_t *sv)
+{
+ sv->capa |= MCPC_CAPA_TOOL_LISTCHG;
+ return MCPC_EC_0;
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_server_capa_enable_rsc (mcpc_server_t *sv)
+{
+ sv->capa |= MCPC_CAPA_RSC;
+ return MCPC_EC_0;
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_server_capa_enable_rsc_listchg (mcpc_server_t *sv)
+{
+ sv->capa |= MCPC_CAPA_RSC_LISTCHG;
+ return MCPC_EC_0;
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_server_capa_enable_rsc_subscr (mcpc_server_t *sv)
+{
+ sv->capa |= MCPC_CAPA_RSC_SUBSRC;
+ return MCPC_EC_0;
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_server_capa_enable_complt (mcpc_server_t *sv)
+{
+ sv->capa |= MCPC_CAPA_COMPLT;
+ return MCPC_EC_0;
+}
+
+static inline bool
+_server_is_buggy (const mcpc_server_t *sv)
+{
+ if (sv == nullptr)
+ return true;
+ if (sv->typ == MCPC_SV_NONE)
+ return true;
+ if (sv->toolpool == nullptr)
+ return true;
+
+ return false;
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_server_add_tool (mcpc_server_t *sv, mcpc_tool_t *tool)
+{
+ mcpc_tcallcb_t callback = nullptr;
+ if (_server_is_buggy (sv))
+ return MCPC_EC_BUG;
+ if (tool == nullptr)
+ return MCPC_EC_BUG;
+ if (mcpc_tool_get_call_cb (tool, &callback) != MCPC_EC_0 || callback == nullptr)
+ return MCPC_EC_BUG;
+ mcpc_toolpool_addfre ((mcpc_toolpool_t *) sv->toolpool, tool);
+ return MCPC_EC_0;
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_server_add_prmpt (mcpc_server_t *sv, mcpc_prmpt_t *prmpt)
+{
+ if (_server_is_buggy (sv))
+ return MCPC_EC_BUG;
+ if (prmpt == nullptr)
+ return MCPC_EC_BUG;
+ mcpc_prmptpool_addfre ((mcpc_prmptpool_t *) sv->prmptpool, prmpt);
+ return MCPC_EC_0;
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_server_add_rsc (mcpc_server_t *sv, mcpc_rsc_t *rsc)
+{
+ if (_server_is_buggy (sv))
+ return MCPC_EC_BUG;
+ if (rsc == nullptr)
+ return MCPC_EC_BUG;
+ mcpc_rscpool_addfre ((mcpc_rscpool_t *) sv->rscpool, rsc);
+ return MCPC_EC_0;
+}
+
+bool
+mcpc_server_supp_prompts (const mcpc_server_t *sv)
+{
+ return (sv->capa & MCPC_CAPA_PRMT);
+}
+
+bool
+mcpc_server_supp_prompts_lschg (const mcpc_server_t *sv)
+{
+ return (sv->capa & MCPC_CAPA_PRMT_LISTCHG) == MCPC_CAPA_PRMT_LISTCHG;
+}
+
+bool
+mcpc_server_supp_tools (const mcpc_server_t *sv)
+{
+ return (sv->capa & MCPC_CAPA_TOOL) == MCPC_CAPA_TOOL;
+}
+
+bool
+mcpc_server_supp_tools_lschg (const mcpc_server_t *sv)
+{
+ return (sv->capa & MCPC_CAPA_TOOL_LISTCHG) == MCPC_CAPA_TOOL_LISTCHG;
+}
+
+bool
+mcpc_server_supp_rscs (const mcpc_server_t *sv)
+{
+ return (sv->capa & MCPC_CAPA_RSC) == MCPC_CAPA_RSC;
+}
+
+bool
+mcpc_server_supp_rscs_lschg (const mcpc_server_t *sv)
+{
+ return (sv->capa & MCPC_CAPA_RSC_LISTCHG) == MCPC_CAPA_RSC_LISTCHG;
+}
+
+bool
+mcpc_server_supp_rscs_subs (const mcpc_server_t *sv)
+{
+ return (sv->capa & MCPC_CAPA_RSC_SUBSRC) == MCPC_CAPA_RSC_SUBSRC;
+}
+
+bool
+mcpc_server_supp_complt (const mcpc_server_t *sv)
+{
+ return (sv->capa & MCPC_CAPA_COMPLT) == MCPC_CAPA_COMPLT;
+}
+
+
+// ----------------------------- iostrm -----------------------------
+
+mcpc_errcode_t
+_wait_iostrm (mcpc_server_t *sv)
+{
+ mcpc_conn_t *conn0 = mcpc_connpool_getconn (sv->connpool, 0);
+ mcpc_assert (conn0->typ == MCPC_CONN_STRM, MCPC_EC_BUG);
+ mcpc_conn_t *conn1 = mcpc_connpool_getconn (sv->connpool, 1);
+ mcpc_assert (conn1->typ == MCPC_CONN_STRM, MCPC_EC_BUG);
+
+ FILE *in_strm = mcpc_conn_getstrm (conn0);
+ mcpc_assert (in_strm != nullptr, MCPC_EC_BUG);
+ FILE *out_strm = mcpc_conn_getstrm (conn1);
+ mcpc_assert (out_strm != nullptr, MCPC_EC_BUG);
+
+ size_t rbuf_cap = BUFCAP_INIT_JSONREQ;
+ size_t rbuf_len = 0;
+ char *rbuf = mcpc_alloc (rbuf_cap);
+ int ch;
+ if (rbuf == nullptr)
+ return MCPC_EC_BUFCAP;
+
+ while ((ch = getc (in_strm)) != EOF)
+ {
+ if (ch == '\n')
+ {
+ if (rbuf_len > 0 && rbuf[rbuf_len - 1] == '\r')
+ rbuf_len -= 1;
+ if (rbuf_len > 0)
+ handle_inone (sv, rbuf, rbuf_len, (mcpc_sock_t) - 1);
+ rbuf_len = 0;
+ continue;
+ }
+ if (rbuf_len == rbuf_cap)
+ {
+ char *grown;
+ if (rbuf_cap > SIZE_MAX / 2)
+ {
+ free (rbuf);
+ return MCPC_EC_BUFCAP;
+ }
+ grown = mcpc_realloc (rbuf, rbuf_cap * 2);
+ if (grown == nullptr)
+ {
+ free (rbuf);
+ return MCPC_EC_BUFCAP;
+ }
+ rbuf = grown;
+ rbuf_cap *= 2;
+ }
+ rbuf[rbuf_len++] = (char) ch;
+ }
+ if (ferror (in_strm))
+ {
+ free (rbuf);
+ return MCPC_EC_BUG;
+ }
+ if (rbuf_len > 0)
+ handle_inone (sv, rbuf, rbuf_len, (mcpc_sock_t) - 1);
+ free (rbuf);
+ return MCPC_EC_0;
+}
+
+mcpc_errcode_t
+_svimpl_iostrm (mcpc_server_t *sv)
+{
+ mcpc_assert (sv->connpool->len == 2, MCPC_EC_BUG);
+ jsonrpc_init (nullptr, nullptr); // no need to handle responses
+
+ return _wait_iostrm (sv);
+}
+
+mcpc_errcode_t
+_start_iostrm (mcpc_server_t *sv)
+{
+ return _svimpl_iostrm (sv);
+}
+
+// ------------------------------ tcp ------------------------------
+
+typedef struct
+{
+ mcpc_sock_t csock;
+ struct sockaddr_in caddr;
+ mcpc_server_t *sv;
+} accept_hdlr_args_t;
+
+mcpc_errcode_t
+_new_tcp_listen (mcpc_sock_t *ssock)
+{
+ mcpc_sock_t tmpsock = (mcpc_sock_t) 0;
+ struct sockaddr_in saddr;
+
+ tmpsock = socket (AF_INET, SOCK_STREAM, 0);
+#if is_unix
+ if (tmpsock < 0)
+#endif
+#if is_win
+ if (tmpsock == INVALID_SOCKET)
+#endif
+ {
+ return MCPC_EC_SVIMPL_SOCKET;
+ }
+
+ int level = SOL_SOCKET;
+ int optname = SO_REUSEADDR;
+ int optval = 1;
+ mcpc_assert (0 == setsockopt (tmpsock, level, optname, (const char *) &optval, sizeof (optval)), MCPC_EC_BUG);
+
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = htons (PORT);
+#if defined(is_unix)
+ inet_pton (AF_INET, "127.0.0.1", &saddr.sin_addr);
+#elif defined(is_win)
+ saddr.sin_addr.s_addr = inet_addr ("127.0.0.1");
+#endif
+
+ if (bind (tmpsock, (struct sockaddr *) &saddr, sizeof (saddr)) == -1)
+ {
+ return MCPC_EC_SVIMPL_BIND;
+ }
+
+ if (listen (tmpsock, 16) == -1)
+ {
+ return MCPC_EC_SVIMPL_LISTEN;
+ }
+
+ *ssock = tmpsock;
+
+ return MCPC_EC_0;
+}
+
+#ifdef is_unix
+static void *
+#endif
+#ifdef is_win
+ DWORD WINAPI
+#endif
+_wait_conn (void *args)
+{
+ accept_hdlr_args_t *thread_args_ptr = (accept_hdlr_args_t *) args;
+ mcpc_sock_t csock = thread_args_ptr->csock;
+#ifdef MCPC_DBG
+ struct sockaddr_in caddr = thread_args_ptr->caddr;
+#endif
+ mcpc_server_t *sv = thread_args_ptr->sv;
+
+ tulog_d ("New connection from client IP address %s and port %d", inet_ntoa (caddr.sin_addr), ntohs (caddr.sin_port));
+
+ char ch;
+ int n_brace = 0;
+ bool start_read = false;
+ bool enter_str = false;
+ size_t rbuf_cap = BUFCAP_INIT_JSONREQ;
+ char *rbuf = mcpc_alloc (rbuf_cap);
+ char *rbuf_p = rbuf;
+ i32_t last_nrecv = 0;
+ char last_read = 0;
+ char lastlast_read = 0;
+
+ while (true)
+ {
+ last_nrecv = recv (csock, &ch, 1, 0);
+ if (false)
+ ;
+ else if (last_nrecv == 0)
+ {
+ break;
+ }
+ else if (last_nrecv < 0)
+ {
+ tulog_d ("recv err %s", strerror (errno));
+ break;
+ }
+
+ if (!enter_str && ch == CH_LBRC)
+ {
+ n_brace += 1;
+ if (!start_read)
+ start_read = true;
+ }
+
+ if (!start_read)
+ {
+ continue;
+ }
+
+ if (start_read)
+ {
+ *rbuf_p = ch;
+ rbuf_p += 1;
+ lastlast_read = last_read;
+ last_read = ch;
+ if (rbuf_p - rbuf >= (ptrdiff_t) rbuf_cap)
+ {
+ rbuf_cap += rbuf_cap;
+ rbuf = mcpc_realloc (rbuf, rbuf_cap);
+ }
+ }
+
+ if (!enter_str && last_read == CH_RBRC)
+ {
+ n_brace -= 1;
+ if (n_brace == 0)
+ {
+ ptrdiff_t nread = rbuf_p - rbuf;
+ rbuf[nread - 1] = CH_RBRC;
+ rbuf[nread] = 0x00;
+ handle_inone (sv, rbuf, (size_t) nread, csock);
+ rbuf_p = rbuf;
+ start_read = false;
+ enter_str = false;
+ tulog_d ("one msg handled");
+ }
+ }
+ if (last_read == CH_QUO && lastlast_read != CH_BSLASH)
+ {
+ enter_str = !enter_str;
+ }
+ }
+
+ free (rbuf);
+
+#ifdef is_unix
+ close (csock);
+#endif
+#ifdef is_win
+ closesocket (csock);
+#endif
+
+ free (thread_args_ptr);
+
+#ifdef is_unix
+ tulog_d ("thread %lu exiting...", pthread_self ());
+#endif
+#ifdef is_win
+ tulog_d ("thread %lu exiting...", GetCurrentThreadId ());
+#endif
+
+#ifdef is_unix
+ pthread_exit (nullptr);
+#endif
+#ifdef is_win
+ ExitThread (0);
+#endif
+}
+
+mcpc_errcode_t
+_svimpl_tcp (mcpc_server_t *sv)
+{
+
+#ifdef is_win
+ WSADATA wsaData;
+ if (WSAStartup (MAKEWORD (2, 2), &wsaData) != 0)
+ {
+ return MCPC_EC_SVIMPL_SOCKET;
+ }
+#endif
+
+ mcpc_sock_t ssock;
+ struct sockaddr_in caddr;
+ socklen_t caddr_length = sizeof (caddr);
+
+ mcpc_assert (MCPC_EC_0 == _new_tcp_listen (&ssock), MCPC_EC_BUG);
+
+ tulog_d ("Server listening on port %d...", PORT);
+
+ mcpc_sock_t csock;
+ while (true)
+ {
+ csock = accept (ssock, (struct sockaddr *) &caddr, &caddr_length);
+#ifdef is_unix
+ if (csock < 0)
+#endif
+#ifdef is_win
+ if (csock == INVALID_SOCKET)
+#endif
+ {
+ tulog_d ("accept failed");
+ continue;
+ }
+
+ accept_hdlr_args_t *args = (accept_hdlr_args_t *) mcpc_alloc (sizeof (accept_hdlr_args_t));
+ args->csock = csock;
+ args->caddr = caddr;
+ args->sv = sv;
+
+#ifdef is_unix
+ pthread_t thread;
+ if (pthread_create (&thread, nullptr, _wait_conn, args) != 0)
+ {
+ tulog_d ("thread creation failed");
+ free (args);
+ close (csock);
+ }
+#endif
+#ifdef is_win
+ HANDLE thread;
+ DWORD threadId;
+ thread = CreateThread (NULL, 0, _wait_conn, args, 0, &threadId);
+ if (thread == NULL)
+ {
+ tulog_d ("thread creation failed");
+ free (args);
+ closesocket (csock);
+ }
+#endif
+ }
+
+#ifdef is_win
+ WSACleanup ();
+#endif
+
+ return 0;
+}
+
+mcpc_errcode_t
+_start_tcp (mcpc_server_t *sv)
+{
+ return _svimpl_tcp (sv);
+}
+
+// ------------------------------- - -------------------------------
+
+MCPC_API mcpc_errcode_t
+mcpc_server_start (mcpc_server_t *sv)
+{
+ if (_server_is_buggy (sv))
+ {
+ return MCPC_EC_BUG;
+ }
+ if (false)
+ ;
+ else if (sv->typ == MCPC_SV_IOSTRM)
+ {
+ return _start_iostrm (sv);
+ }
+ else if (sv->typ == MCPC_SV_TCP)
+ {
+ return _start_tcp (sv);
+ }
+ else
+ {
+ return MCPC_EC_BUG;
+ }
+ return MCPC_EC_0;
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_server_close (mcpc_server_t *sv)
+{
+ // return MCPC_EC_0; // NOTE: this does not trigger asan but valgrind
+ if (_server_is_buggy (sv))
+ {
+ return MCPC_EC_BUG;
+ }
+
+ if (sv->name != nullptr)
+ {
+ free ((void *) sv->name);
+ sv->name = nullptr;
+ sv->name_len = 0;
+ }
+
+ if (sv->ver != nullptr)
+ {
+ free ((void *) sv->ver);
+ sv->ver = nullptr;
+ sv->ver_len = 0;
+ }
+
+ free (sv->rpcres.ptr);
+ mcpc_toolpool_free ((mcpc_toolpool_t *) sv->toolpool);
+ mcpc_rscpool_free ((mcpc_rscpool_t *) sv->prmptpool);
+ mcpc_rscpool_free ((mcpc_rscpool_t *) sv->rscpool);
+ mcpc_connpool_free ((mcpc_connpool_t *) sv->connpool);
+
+ free (sv);
+
+ return MCPC_EC_0;
+}
+
+mcpc_errcode_t
+mcpc_server_cleanup_rpcres (mcpc_server_t *sv)
+{
+ sv->rpcres.len = 0;
+ return MCPC_EC_0;
+}
+
+bool
+_connpool_is_addable (mcpc_connpool_t *connpool)
+{
+ if (connpool->len < connpool->cap)
+ {
+ return true;
+ }
+ return false;
+}
+
+mcpc_conn_t *
+mcpc_conn_new (mcpc_conn_type_t typ, void *back)
+{
+ mcpc_conn_t *conn = mcpc_alloc (sizeof (mcpc_conn_t));
+ conn->typ = typ;
+
+ if (false)
+ ;
+ else if (typ == MCPC_CONN_STRM)
+ {
+ FILE *strm = (FILE *) back;
+ conn->strm = strm;
+ }
+ else if (typ == MCPC_CONN_FD)
+ {
+ int fd = *((int *) back);
+ conn->fd = fd;
+ }
+ else
+ {
+ conn->strm = nullptr;
+ }
+ conn->cname = nullptr;
+ conn->cname_len = 0;
+ conn->client_init = MCPC_INIT_NONE;
+ conn->nex = nullptr;
+
+ return conn;
+}
+
+FILE *
+mcpc_conn_getstrm (mcpc_conn_t *conn)
+{
+ if (conn->typ != MCPC_CONN_STRM)
+ {
+ return nullptr;
+ }
+ return conn->strm;
+}
+
+int
+mcpc_conn_getfd (mcpc_conn_t *conn)
+{
+ if (conn->typ != MCPC_CONN_FD)
+ {
+ return 0;
+ }
+ return conn->fd & ((1ULL << (sizeof (int) * 8)) - 1);
+}
+
+mcpc_errcode_t
+mcpc_connpool_addref (mcpc_connpool_t *connpool, mcpc_conn_t *conn)
+{
+ if (_connpool_is_buggy ((const mcpc_connpool_t *) connpool))
+ {
+ return MCPC_EC_BUG;
+ }
+
+ if (!_connpool_is_addable (connpool))
+ {
+ return MCPC_EC_CONNPOOL_NOTADD;
+ }
+
+ if (connpool->head == nullptr)
+ {
+ connpool->head = conn;
+ connpool->len = 1;
+ }
+ else
+ {
+ mcpc_conn_t *cur_conn = connpool->head;
+ while (cur_conn->nex != nullptr)
+ {
+ cur_conn = cur_conn->nex;
+ }
+ cur_conn->nex = conn;
+ connpool->len += 1;
+ // mcpc_assert(!ckd_add(&connpool->len, connpool->len, 1), MCPC_EC_BUG);
+ }
+
+ return MCPC_EC_0;
+}
+
+mcpc_conn_t *
+mcpc_connpool_getconn (const mcpc_connpool_t *connpool, const mcpc_connpool_size_t idx)
+{
+ mcpc_assert (!_connpool_is_buggy (connpool), MCPC_EC_BUG);
+ mcpc_connpool_size_t cur_idx = 0;
+ mcpc_conn_t *cur_conn = connpool->head;
+ while (cur_conn != nullptr && cur_idx < idx)
+ {
+ cur_conn = cur_conn->nex;
+ cur_idx += 1;
+ // mcpc_assert(!ckd_add(&cur_idx, cur_idx, 1), MCPC_EC_BUG);
+ }
+ return cur_conn;
+}
+
+mcpc_conn_t *
+mcpc_connpool_find_by_sock (const mcpc_connpool_t *connpool, mcpc_sock_t sock)
+{
+ mcpc_assert (!_connpool_is_buggy (connpool), MCPC_EC_BUG);
+
+ // iostrm
+ if (mcpc_sock_invalid (sock))
+ {
+ return connpool->head;
+ }
+
+ // tcp
+ mcpc_conn_t *cur_conn = connpool->head;
+ while (cur_conn != nullptr)
+ {
+ mcpc_sock_t cur_sock = cur_conn->fd;
+ if (cur_sock == sock)
+ {
+ return cur_conn;
+ }
+ cur_conn = cur_conn->nex;
+ }
+
+ return nullptr;
+}
+
+mcpc_errcode_t
+mcpc_conn_set_cname (mcpc_conn_t *conn, const ch8_t *const cname, const u8_t cname_len)
+{
+ void *hp = mcpc_alloc (cname_len);
+ memcpy (hp, cname, cname_len);
+ conn->cname = hp;
+ conn->cname_len = cname_len;
+ return MCPC_EC_0;
+}
+
+void
+_handle_initbeg (mcpc_server_t *sv, struct jsonrpc_request *r, mcpc_sock_t csock)
+{
+
+ mcpc_conn_t *conn = mcpc_connpool_find_by_sock (sv->connpool, csock);
+ if (conn == nullptr)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "connection not found", NULL);
+ goto output_res;
+ }
+ if (conn->client_init == MCPC_INIT_SUCC)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "conn already initialized", NULL);
+ goto output_res;
+ }
+
+ // - - -
+
+ const char8_t *protover = nullptr;
+ size_t protover_len = 0;
+ if (MCPC_EC_0 != _tulc_jparams_getref_field_u8str_q (r, u8c1 ("$.protocolVersion"), &protover, &protover_len))
+ return;
+
+ // - - - client info
+
+ char8_t *cinfo_name = nullptr;
+ size_t cinfo_name_len = 0;
+ char8_t *cinfo_ver = nullptr;
+ size_t cinfo_ver_len = 0;
+
+ cinfo_name = alloca (128);
+ if (MCPC_EC_0 !=
+ _tulc_jparams_get_field_u8str (r, u8c1 ("$.clientInfo.name"), &cinfo_name, &cinfo_name_len, 128, true))
+ return; // TODO P5 add test
+
+ cinfo_ver = alloca (128);
+ if (MCPC_EC_0 !=
+ _tulc_jparams_get_field_u8str (r, u8c1 ("$.clientInfo.version"), &cinfo_ver, &cinfo_ver_len, 128, true))
+ return; // TODO P5 add test
+
+ mcpc_conn_set_cname (conn, cinfo_name, (u8_t) cinfo_name_len);
+
+ // - - - capabilities
+
+ const char8_t *ccapas = nullptr;
+ size_t ccapas_len = 0;
+ if (MCPC_EC_0 != _tulc_jparams_getref_field_obj (r, u8c1 ("$.capabilities"), &ccapas, &ccapas_len))
+ return; // TODO P5 add test
+
+ // TODO P6 capabilities negotiation
+
+ char *buftpool = nullptr;
+ size_t buftpool_len = 0;
+ const size_t buftpool_cap = 65536;
+ buftpool = mcpc_alloc (buftpool_cap);
+ retbuf_t rtbf = { buftpool_cap, buftpool, &buftpool_len };
+ mcpc_assert (!serlz_initres (rtbf, sv), MCPC_EC_BUG);
+ mcpc_assert (buftpool != nullptr, MCPC_EC_BUG);
+ mcpc_assert (buftpool_len > 0, MCPC_EC_BUG);
+ jsonrpc_return_success2 (r, mcpc_sock_invalid (csock), "%.*s", buftpool_len, buftpool);
+ free (buftpool); // TODO intn_free
+
+ conn->client_init = MCPC_INIT_BEG;
+
+output_res:
+ return;
+}
+
+void
+_handle_initdone (mcpc_server_t *sv, struct jsonrpc_request *r, mcpc_sock_t csock, bool *noreply)
+{
+
+ mcpc_conn_t *conn = mcpc_connpool_find_by_sock (sv->connpool, csock);
+ if (conn == nullptr)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "connection not found", NULL);
+ goto output_res;
+ }
+ if (conn->client_init != MCPC_INIT_BEG)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "client not begin init", NULL);
+ goto output_res;
+ }
+
+ conn->client_init = MCPC_INIT_SUCC;
+ *noreply = true;
+
+output_res:
+ return;
+}
+
+void
+_handle_tools_list (mcpc_server_t *sv, struct jsonrpc_request *r, mcpc_sock_t csock)
+{
+
+ mcpc_conn_t *conn = mcpc_connpool_find_by_sock (sv->connpool, csock);
+ if (conn == nullptr)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "connection not found", NULL);
+ goto output_res;
+ }
+ if (conn->client_init != MCPC_INIT_SUCC)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "client not initialized", NULL);
+ goto output_res;
+ }
+
+ char *buftpool = nullptr;
+ size_t buftpool_len = 0;
+ const size_t buftpool_cap = 65536;
+ buftpool = mcpc_alloc (buftpool_cap);
+ retbuf_t rtbf = { buftpool_cap, buftpool, &buftpool_len };
+ mcpc_assert (!serlz_toolpool (rtbf, sv->toolpool), MCPC_EC_BUG);
+ mcpc_assert (buftpool != nullptr, MCPC_EC_BUG);
+ mcpc_assert (buftpool_len > 0, MCPC_EC_BUG);
+ jsonrpc_return_success2 (r, mcpc_sock_invalid (csock), "%.*s", buftpool_len, buftpool);
+ free (buftpool); // TODO intn_free
+
+output_res:
+ return;
+}
+
+void
+_handle_prmpt_list (mcpc_server_t *sv, struct jsonrpc_request *r, mcpc_sock_t csock)
+{
+ if (!mcpc_server_supp_prompts (sv))
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "prompts is not supported", NULL);
+ goto output_res;
+ }
+
+ mcpc_conn_t *conn = mcpc_connpool_find_by_sock (sv->connpool, csock);
+ if (conn == nullptr)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "connection not found", NULL);
+ goto output_res;
+ }
+ if (conn->client_init != MCPC_INIT_SUCC)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "client not initialized", NULL);
+ goto output_res;
+ }
+
+ char *buftpool = nullptr;
+ size_t buftpool_len = 0;
+ const size_t buftpool_cap = 1024;
+ buftpool = mcpc_alloc (buftpool_cap);
+ retbuf_t rtbf = { buftpool_cap, buftpool, &buftpool_len };
+ mcpc_assert (!serlz_prmptpool (rtbf, sv->prmptpool), MCPC_EC_BUG);
+ mcpc_assert (buftpool != nullptr, MCPC_EC_BUG);
+ mcpc_assert (buftpool_len > 0, MCPC_EC_BUG);
+ jsonrpc_return_success2 (r, mcpc_sock_invalid (csock), "%.*s", buftpool_len, buftpool);
+ free (buftpool); // TODO intn_free
+
+output_res:
+ return;
+}
+
+void
+_handle_lsrsc (mcpc_server_t *sv, struct jsonrpc_request *r, mcpc_sock_t csock)
+{
+
+ mcpc_conn_t *conn = mcpc_connpool_find_by_sock (sv->connpool, csock);
+ if (conn == nullptr)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "connection not found", NULL);
+ goto output_res;
+ }
+ if (conn->client_init != MCPC_INIT_SUCC)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "client not initialized", NULL);
+ goto output_res;
+ }
+
+ char *buftpool = nullptr;
+ size_t buftpool_len = 0;
+ const size_t buftpool_cap = 1024;
+ buftpool = mcpc_alloc (buftpool_cap);
+ retbuf_t rtbf = { buftpool_cap, buftpool, &buftpool_len };
+ mcpc_assert (!serlz_rscpool (rtbf, sv->rscpool), MCPC_EC_BUG);
+ mcpc_assert (buftpool != nullptr, MCPC_EC_BUG);
+ mcpc_assert (buftpool_len > 0, MCPC_EC_BUG);
+ jsonrpc_return_success2 (r, mcpc_sock_invalid (csock), "%.*s", buftpool_len, buftpool);
+ free (buftpool); // TODO intn_free
+
+output_res:
+ return;
+}
+
+void
+_handle_tools_call (mcpc_server_t *sv, struct jsonrpc_request *r, mcpc_sock_t csock)
+{
+ mcpc_ucbr_t *ucbr = nullptr;
+ char *buftpool = nullptr;
+ mcpc_conn_t *conn = mcpc_connpool_find_by_sock (sv->connpool, csock);
+ if (conn == nullptr)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "connection not found", NULL);
+ goto output_res;
+ }
+ if (conn->client_init != MCPC_INIT_SUCC)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "client not initialized", NULL);
+ goto output_res;
+ }
+
+ char8_t *callname = nullptr; // TODO suff?
+ size_t callname_len = 0;
+
+ callname = alloca (1024); // TODO insuffi?
+ if (MCPC_EC_0 != _tulc_jparams_get_field_u8str (r, u8c1 ("$.name"), &callname, &callname_len, 1024, true))
+ return;
+
+ // const mcpc_tool_t *tool = mcpc_toolpool_find_tool (sv->toolpool, callname, callname_len);
+
+ const mcpc_tool_t *tool = (const mcpc_tool_t *) mcpc_toolpool_getbyname (sv->toolpool, callname, callname_len);
+ if (tool == nullptr)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_BAD_PARAMS, "tool not found", NULL);
+ goto output_res;
+ }
+
+ const char8_t *callargs = nullptr;
+ size_t callargs_len = 0;
+ if (MCPC_EC_0 != _tulc_jparams_getref_field_obj (r, u8c1 ("$.arguments"), &callargs, &callargs_len))
+ goto output_res;
+
+ size_t property_count = mcpc_tool_get_toolprop_len (tool);
+ for (size_t property_index = 0; property_index < property_count; ++property_index)
+ {
+ mcpc_toolprop_t *cur_tprop
+ = (mcpc_toolprop_t *) mcpc_tool_get_toolprop (tool, property_index);
+ const char8_t *cur_tp_name = nullptr;
+ size_t cur_tp_name_len = 0;
+ bug_if_nz (mcpc_toolprop_getref_name (cur_tprop, &cur_tp_name, &cur_tp_name_len));
+ mcpc_anytype_t cur_tp_type = MCPC_NONE;
+ bug_if_nz (mcpc_toolprop_get_data_type (cur_tprop, &cur_tp_type));
+ char8_t *cur_tp_jpath = alloca (cur_tp_name_len + 3);
+ const char *propval_q = nullptr;
+ int propval_q_len = 0;
+ int propval_type;
+ snprintf ((char *) cur_tp_jpath, cur_tp_name_len + 3, "$.%.*s", (int) cur_tp_name_len, cur_tp_name);
+ cur_tprop->flags &= ~ANYDATA_FL_PRESENT;
+ propval_type = mjson_find ((const char *) callargs, (int) callargs_len,
+ (const char *) cur_tp_jpath, &propval_q, &propval_q_len);
+ if (propval_type == MJSON_TOK_INVALID)
+ continue;
+ if (false)
+ ;
+ else if (cur_tp_type == MCPC_I32)
+ {
+ double propval = 0;
+ if (propval_type != MJSON_TOK_NUMBER
+ || mjson_get_number (propval_q, propval_q_len, "$", &propval) != 1 || !isfinite (propval)
+ || trunc (propval) != propval || propval < INT32_MIN || propval > INT32_MAX)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_BAD_PARAMS, "integer argument is invalid", "%.*Q",
+ cur_tp_name_len, cur_tp_name);
+ goto output_res;
+ }
+ mcpc_toolprop_set_i32 (cur_tprop, (i32_t) propval);
+ }
+ else if (cur_tp_type == MCPC_U8STR)
+ {
+ char8_t *propval;
+ int propval_len;
+ if (propval_type != MJSON_TOK_STRING)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_BAD_PARAMS, "string argument is invalid", "%.*Q",
+ cur_tp_name_len, cur_tp_name);
+ goto output_res;
+ }
+ propval = mcpc_alloc ((size_t) propval_q_len + 1);
+ if (propval == nullptr)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_INTERNAL, "out of memory", NULL);
+ goto output_res;
+ }
+ propval_len = mjson_get_string (propval_q, propval_q_len, "$", (char *) propval,
+ (size_t) propval_q_len + 1);
+ if (propval_len < 0)
+ {
+ free (propval);
+ jsonrpc_return_error (r, JSONRPC_ERROR_BAD_PARAMS, "string argument is invalid", NULL);
+ goto output_res;
+ }
+ mcpc_toolprop_set_u8str (cur_tprop, propval, (size_t) propval_len);
+ free (propval);
+ }
+ else
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_INTERNAL, "unsupported argument type", NULL);
+ goto output_res;
+ }
+ }
+
+ ucbr = mcpc_ucbr_new ();
+ if (ucbr == nullptr)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_INTERNAL, "out of memory", NULL);
+ goto output_res;
+ }
+
+ mcpc_tcallcb_t callcb = nullptr;
+ if (mcpc_tool_get_call_cb (tool, &callcb) != MCPC_EC_0 || callcb == nullptr)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_INTERNAL, "tool callback is unavailable", NULL);
+ goto output_res;
+ }
+ callcb (tool, ucbr);
+
+ size_t buftpool_len = 0;
+ const size_t buftpool_cap = mcpc_ucbr_get_serlz_lenhint (ucbr);
+ if (buftpool_cap == 0)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_INTERNAL, "response is too large", NULL);
+ goto output_res;
+ }
+ buftpool = mcpc_alloc (buftpool_cap);
+ if (buftpool == nullptr)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_INTERNAL, "out of memory", NULL);
+ goto output_res;
+ }
+ retbuf_t rtbf = { buftpool_cap, buftpool, &buftpool_len };
+
+ serlz_tool_callres (rtbf, ucbr);
+ if (buftpool_len == 0 || buftpool_len >= buftpool_cap)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_INTERNAL, "response serialization failed", NULL);
+ goto output_res;
+ }
+
+ if (!ensure_buf_suffi (&sv->rpcres, buftpool_cap))
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_INTERNAL, "response buffer allocation failed", NULL);
+ goto output_res;
+ }
+ jsonrpc_return_success2 (r, mcpc_sock_invalid (csock), "%.*s", buftpool_len, buftpool);
+
+output_res:
+ if (ucbr != nullptr)
+ mcpc_ucbr_free (ucbr);
+ free (buftpool);
+ return;
+}
+
+void
+_handle_prmpt_call (mcpc_server_t *sv, struct jsonrpc_request *r, mcpc_sock_t csock)
+{
+ if (!mcpc_server_supp_prompts (sv))
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "prompts is not supported", NULL);
+ goto output_res;
+ }
+
+ mcpc_conn_t *conn = mcpc_connpool_find_by_sock (sv->connpool, csock);
+ if (conn == nullptr)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "connection not found", NULL);
+ goto output_res;
+ }
+ if (conn->client_init != MCPC_INIT_SUCC)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "client not initialized", NULL);
+ goto output_res;
+ }
+
+ char8_t *callname = nullptr;
+ size_t callname_len = 0;
+
+ callname = alloca (128); // TODO insuffi?
+ if (MCPC_EC_0 != _tulc_jparams_get_field_u8str (r, u8c1 ("$.name"), &callname, &callname_len, 1024, true))
+ return;
+
+ bug_if (callname_len == 0);
+
+ const mcpc_prmpt_t *prmpt = (const mcpc_prmpt_t *) mcpc_prmptpool_getbyname (sv->prmptpool, callname, callname_len);
+ if (prmpt == nullptr)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "prmopt not found", NULL);
+ goto output_res;
+ }
+
+
+ const char8_t *callargs = nullptr;
+ size_t callargs_len = 0;
+ callargs = alloca (1024); // TODO insuffi?
+ if (MCPC_EC_0 != _tulc_jparams_getref_field_obj (r, u8c1 ("$.arguments"), &callargs, &callargs_len))
+ return;
+
+ const mcpc_prmptarg_t *cur_parg = nullptr;
+ while ((cur_parg = mcpc_prmpt_get_next_prmptarg (prmpt)))
+ {
+ const char8_t *cur_tp_name = nullptr;
+ size_t cur_tp_name_len = 0;
+ bug_if_nz (mcpc_prmptarg_getref_name (cur_parg, &cur_tp_name, &cur_tp_name_len));
+ mcpc_anytype_t cur_tp_type = MCPC_NONE;
+ bug_if_nz (mcpc_prmptarg_get_data_type (cur_parg, &cur_tp_type));
+ char8_t *cur_tp_jpath = alloca (100);
+ sprintf ((char *) cur_tp_jpath, "$.%.*s", (int) cur_tp_name_len, cur_tp_name);
+ if (false)
+ ;
+ else if (cur_tp_type == MCPC_U8STR)
+ {
+ char8_t *propval = alloca (1024); // TODO might be insuff
+ size_t propval_len = 0;
+
+ // copy is essential here
+ if (MCPC_EC_0 !=
+ _tulc_jstr_get_field_u8str (r, callargs, callargs_len, cur_tp_jpath, &propval, &propval_len, 1024, true))
+ return;
+
+ mcpc_prmptarg_set_u8str ((mcpc_prmptarg_t *) cur_parg, propval, (size_t) propval_len);
+ }
+ }
+
+ mcpc_ucbr_t *ucbr = mcpc_ucbr_new ();
+
+ mcpc_prmpt_callcb_t callcb = nullptr;
+ mcpc_prmpt_get_callcb (prmpt, &callcb);
+ callcb (prmpt, ucbr);
+
+ char *buftpool = nullptr;
+ size_t buftpool_len = 0;
+ const size_t buftpool_cap = 1024;
+ buftpool = mcpc_alloc (buftpool_cap);
+ retbuf_t rtbf = { buftpool_cap, buftpool, &buftpool_len };
+
+ serlz_prmpt_callres (rtbf, ucbr);
+ mcpc_assert (buftpool != nullptr, MCPC_EC_BUG);
+ mcpc_assert (buftpool_len > 0, MCPC_EC_BUG);
+
+ jsonrpc_return_success2 (r, mcpc_sock_invalid (csock), "%.*s", buftpool_len, buftpool);
+
+ mcpc_ucbr_free (ucbr);
+ free (buftpool); // TODO intn_free
+
+output_res:
+ return;
+}
+
+
+void
+_handle_complt_complt (mcpc_server_t *sv, struct jsonrpc_request *r, mcpc_sock_t csock)
+{
+ if (!mcpc_server_supp_complt (sv))
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "completion is not supported", NULL);
+ goto output_res;
+ }
+
+ mcpc_conn_t *conn = mcpc_connpool_find_by_sock (sv->connpool, csock);
+ if (conn == nullptr)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "connection not found", NULL);
+ goto output_res;
+ }
+ if (conn->client_init != MCPC_INIT_SUCC)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "client not initialized", NULL);
+ goto output_res;
+ }
+
+
+ // ref.type
+ char8_t *compref_type = alloca (1024); // TODO suff?
+ size_t compref_type_len = 0;
+ if (MCPC_EC_0 != _tulc_jparams_get_field_u8str (r, u8c1 ("$.ref.type"), &compref_type, &compref_type_len, 1024, true))
+ return;
+
+ int askfor_what = -1;
+
+ if (false);
+ else if (0 == u8strncmp (compref_type, u8c1 ("ref/prompt"), compref_type_len))
+ askfor_what = 1;
+ else if (0 == u8strncmp (compref_type, u8c1 ("ref/resource"), compref_type_len))
+ askfor_what = 2;
+
+ if (askfor_what < 0)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "<ref.type> invalid", NULL);
+ return;
+ }
+
+ char8_t *compref_name = nullptr; // TODO suff?
+ size_t compref_name_len = 0;
+ char8_t *compref_uri = nullptr; // TODO suff?
+ size_t compref_uri_len = 0;
+ char8_t *comparg_name = nullptr; // TODO suff?
+ size_t comparg_name_len = 0;
+ char8_t *comparg_val = nullptr; // TODO suff?
+ size_t comparg_val_len = 0;
+
+ bool askfor_prmpt = askfor_what == 1;
+ bool askfor_resc = askfor_what == 2;
+
+ mcpc_complt_t *complt = nullptr;
+
+ if (false);
+ else if (askfor_prmpt)
+ {
+ // ref.name
+ compref_name = alloca (1024); // TODO insuffi?
+ if (MCPC_EC_0 !=
+ _tulc_jparams_get_field_u8str (r, u8c1 ("$.ref.name"), &compref_name, &compref_name_len, 1024, true))
+ return;
+
+ // argument.name
+ comparg_name = alloca (1024); // TODO insuffi?
+ if (MCPC_EC_0 !=
+ _tulc_jparams_get_field_u8str (r, u8c1 ("$.argument.name"), &comparg_name, &comparg_name_len, 1024, true))
+ return;
+
+ // argument.value
+ comparg_val = alloca (1024); // TODO insuffi?
+ if (MCPC_EC_0 !=
+ _tulc_jparams_get_field_u8str (r, u8c1 ("$.argument.value"), &comparg_val, &comparg_val_len, 1024, false))
+ return;
+
+ const mcpc_prmpt_t *tgt_prmpt = mcpc_prmptpool_getbyname (sv->prmptpool, compref_name, compref_name_len);
+ if (tgt_prmpt == nullptr)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "prompt not found", "%.*Q", compref_name_len, compref_name);
+ return;
+ }
+
+ const mcpc_prmptarg_t *tgt_prmptarg = mcpc_prmpt_getbyname_prmptarg (tgt_prmpt, comparg_name, comparg_name_len);
+ if (tgt_prmptarg == nullptr)
+ {
+ jsonrpc_return_error (r, JSONRPC_ERROR_NOT_FOUND, "promptarg not found", "%.*Q", comparg_name_len,
+ comparg_name);
+ return;
+ }
+
+ complt = mcpc_complt_new_prmptarg (tgt_prmpt, comparg_name, comparg_name_len, comparg_val, comparg_val_len);
+ }
+ else if (askfor_resc)
+ {
+ // ref.uri mycomm: rubbish design
+ compref_uri = alloca (1024);
+ if (MCPC_EC_0 !=
+ _tulc_jparams_get_field_u8str (r, u8c1 ("$.ref.uri"), &compref_uri, &compref_uri_len, 1024, false))
+ return;
+ complt = mcpc_complt_new_rsc (sv->rscpool, compref_uri, compref_uri_len);
+ }
+
+ bug_if_nullptr (complt);
+
+ char *buftpool = nullptr;
+ size_t buftpool_len = 0;
+ const size_t buftpool_cap = 1024;
+ buftpool = mcpc_alloc (buftpool_cap);
+ retbuf_t rtbf = { buftpool_cap, buftpool, &buftpool_len };
+
+ serlz_complt (rtbf, complt);
+ mcpc_complt_free (complt);
+
+ // buftpool_len =
+ // sprintf (buftpool, " %.*s %.*s %.*s %.*s", (int) comparg_name_len, comparg_name, (int) comparg_val_len, comparg_val,
+ // (int) compref_type_len, compref_type, (int) compref_type_len, compref_type);
+ // strcpy (buftpool, "xxxxx");
+
+ mcpc_assert (buftpool != nullptr, MCPC_EC_BUG);
+ mcpc_assert (buftpool_len > 0, MCPC_EC_BUG);
+
+ jsonrpc_return_success2 (r, mcpc_sock_invalid (csock), "%.*s", buftpool_len, buftpool);
+
+ free (buftpool); // TODO intn_free
+
+output_res:
+ return;
+}
+
+bool
+is_metho_need_params (mcpc_metho_t metho)
+{
+ mcpc_assert (metho != MCPC_METHO_UNKNOWN, MCPC_EC_BUG);
+ if (metho == MCPC_METHO_INITBEG)
+ return true;
+ if (metho == MCPC_METHO_CALLTOOL)
+ return true;
+ if (metho == MCPC_METHO_PRMPT_CALL)
+ return true;
+ if (metho == MCPC_METHO_COMPLT_COMPLT)
+ return true;
+ return false;
+}
+
+void
+handle_inone (mcpc_server_t *sv, const char *const rbuf, size_t nread, mcpc_sock_t csock)
+{
+ bool noreply = false;
+ bool has_id = false;
+ char8_t *methodname = nullptr;
+ int32_t methodname_len = 0;
+ int parsed;
+ size_t offset;
+ tuflog_d ("rbuf:%.*s", nread, rbuf);
+ struct jsonrpc_request r = { 0 };
+ sv->rpcres.len = 0;
+ r.ctx = &jsonrpc_default_context;
+ r.frame = rbuf;
+ r.frame_len = (int) nread;
+ r.fn = mjson_print_fixed_buf;
+ r.fn_data = (void *) &sv->rpcres;
+
+ parsed = nread > INT_MAX ? MJSON_ERROR_INVALID_INPUT : mjson (rbuf, (int) nread, nullptr, nullptr);
+ if (parsed < 0)
+ {
+ jsonrpc_return_error (&r, JSONRPC_ERROR_INVALID, "parse error", NULL);
+ goto output_res;
+ }
+ for (offset = (size_t) parsed; offset < nread; ++offset)
+ if (rbuf[offset] != ' ' && rbuf[offset] != '\t' && rbuf[offset] != '\r')
+ {
+ jsonrpc_return_error (&r, JSONRPC_ERROR_INVALID, "parse error", NULL);
+ goto output_res;
+ }
+ offset = 0;
+ while (offset < nread && (rbuf[offset] == ' ' || rbuf[offset] == '\t' || rbuf[offset] == '\r'))
+ offset += 1;
+ if (offset == nread || rbuf[offset] != '{')
+ {
+ jsonrpc_return_error (&r, JSONRPC_ERROR_INVALID_REQUEST, "invalid request", NULL);
+ goto output_res;
+ }
+
+ const char *jsonrpc_q = nullptr;
+ int jsonrpc_q_len = 0;
+ char jsonrpc[8];
+ if (mjson_find (rbuf, (int) nread, "$.jsonrpc", &jsonrpc_q, &jsonrpc_q_len) != MJSON_TOK_STRING
+ || mjson_get_string (jsonrpc_q, jsonrpc_q_len, "$", jsonrpc, sizeof (jsonrpc)) != 3
+ || strcmp (jsonrpc, "2.0") != 0)
+ {
+ jsonrpc_return_error (&r, JSONRPC_ERROR_INVALID_REQUEST, "jsonrpc must be 2.0", NULL);
+ goto output_res;
+ }
+
+ int id_type = mjson_find (rbuf, (int) nread, "$.id", &r.id, &r.id_len);
+ has_id = id_type != MJSON_TOK_INVALID;
+ if (has_id && id_type != MJSON_TOK_STRING && id_type != MJSON_TOK_NUMBER)
+ {
+ r.id = nullptr;
+ r.id_len = 0;
+ jsonrpc_return_error (&r, JSONRPC_ERROR_INVALID_REQUEST, "id must be a string or number", NULL);
+ goto output_res;
+ }
+
+ const char8_t *methodname_q = nullptr;
+ int32_t methodname_q_len = 0;
+ if (mjson_find (rbuf, (int) nread, "$.method", (const char **) &methodname_q, &methodname_q_len) != MJSON_TOK_STRING)
+ {
+ jsonrpc_return_error (&r, JSONRPC_ERROR_INVALID_REQUEST, "method must be a string", NULL);
+ goto output_res;
+ }
+ if (methodname_q == nullptr || methodname_q_len == 0)
+ {
+ jsonrpc_return_error (&r, JSONRPC_ERROR_INVALID_REQUEST, "method must not be empty", NULL);
+ goto output_res;
+ }
+
+ methodname = mcpc_alloc ((size_t) methodname_q_len + 1);
+ if (methodname == nullptr)
+ {
+ jsonrpc_return_error (&r, JSONRPC_ERROR_INTERNAL, "out of memory", NULL);
+ goto output_res;
+ }
+ methodname_len = mjson_get_string ((const char *) methodname_q, methodname_q_len, "$",
+ (char *) methodname, (size_t) methodname_q_len + 1);
+ if (methodname_len <= 0)
+ {
+ jsonrpc_return_error (&r, JSONRPC_ERROR_INVALID_REQUEST, "method is invalid", NULL);
+ goto output_res;
+ }
+ methodname[methodname_len] = 0;
+
+ mcpc_metho_t handle_what = MCPC_METHO_UNKNOWN; // nothing
+
+ if (false)
+ ;
+ else if (u8streq (methodname, u8c1 ("initialize")))
+ handle_what = MCPC_METHO_INITBEG;
+ else if (u8streq (methodname, u8c1 ("notifications/initialized")))
+ handle_what = MCPC_METHO_INITDONE;
+ else if (u8streq (methodname, u8c1 ("tools/list")))
+ handle_what = MCPC_METHO_LISTTOOL;
+ else if (u8streq (methodname, u8c1 ("tools/call")))
+ handle_what = MCPC_METHO_CALLTOOL;
+ else if (u8streq (methodname, u8c1 ("resources/list")))
+ handle_what = MCPC_METHO_LSRSC;
+ else if (u8streq (methodname, u8c1 ("prompts/list")))
+ handle_what = MCPC_METHO_PRMPT_LS;
+ else if (u8streq (methodname, u8c1 ("prompts/get")))
+ handle_what = MCPC_METHO_PRMPT_CALL;
+ else if (u8streq (methodname, u8c1 ("completion/complete")))
+ handle_what = MCPC_METHO_COMPLT_COMPLT;
+
+ if (handle_what == MCPC_METHO_UNKNOWN)
+ {
+ if (!has_id)
+ noreply = true;
+ else
+ jsonrpc_return_error (&r, JSONRPC_ERROR_NOT_FOUND, "method not found", NULL);
+ goto output_res;
+ }
+
+ const char *params = nullptr;
+ int params_len = 0;
+ int params_type = mjson_find (rbuf, (int) nread, "$.params", &params, &params_len);
+ if (params_type != MJSON_TOK_INVALID && params_type != MJSON_TOK_OBJECT)
+ {
+ if (!has_id)
+ noreply = true;
+ else
+ jsonrpc_return_error (&r, JSONRPC_ERROR_BAD_PARAMS, "params must be an object", NULL);
+ goto output_res;
+ }
+ r.params = params_type == MJSON_TOK_OBJECT ? params : "";
+ r.params_len = params_type == MJSON_TOK_OBJECT ? params_len : 0;
+
+ if (is_metho_need_params (handle_what) && r.params_len < 1)
+ {
+ if (!has_id)
+ noreply = true;
+ else
+ jsonrpc_return_error (&r, JSONRPC_ERROR_BAD_PARAMS, "params are required", NULL);
+ goto output_res;
+ }
+
+ if (!has_id && handle_what != MCPC_METHO_INITDONE)
+ {
+ noreply = true;
+ goto output_res;
+ }
+
+ // Windows SOCKET is unsigned, while Unix file descriptors use a negative
+ // sentinel for stdio dispatch.
+#if is_win
+ if (!mcpc_sock_invalid (csock))
+#elif defined(is_unix)
+ if (csock > 0)
+#endif
+ { // TODO: more accurate?
+ mcpc_conn_t *conn0 = mcpc_conn_new (MCPC_CONN_FD, &csock);
+ mcpc_assert (MCPC_EC_0 == mcpc_connpool_addref ((mcpc_connpool_t *) sv->connpool, conn0), MCPC_EC_BUG);
+ }
+
+ if (false)
+ ;
+ else if (handle_what == MCPC_METHO_INITBEG)
+ _handle_initbeg (sv, &r, csock);
+ else if (handle_what == MCPC_METHO_INITDONE)
+ _handle_initdone (sv, &r, csock, &noreply);
+ else if (handle_what == MCPC_METHO_LISTTOOL)
+ _handle_tools_list (sv, &r, csock);
+ else if (handle_what == MCPC_METHO_LSRSC)
+ _handle_lsrsc (sv, &r, csock);
+ else if (handle_what == MCPC_METHO_CALLTOOL)
+ _handle_tools_call (sv, &r, csock);
+ else if (handle_what == MCPC_METHO_PRMPT_LS)
+ _handle_prmpt_list (sv, &r, csock);
+ else if (handle_what == MCPC_METHO_PRMPT_CALL)
+ _handle_prmpt_call (sv, &r, csock);
+ else if (handle_what == MCPC_METHO_COMPLT_COMPLT)
+ _handle_complt_complt (sv, &r, csock);
+
+output_res:
+ free (methodname);
+ if (noreply)
+ return;
+ if (false)
+ ;
+ else if (sv->typ == MCPC_SV_IOSTRM)
+ {
+ tuflog_d ("sv->rpcres:%.*s", sv->rpcres.len, sv->rpcres.ptr);
+ mcpc_conn_t *outconn = mcpc_connpool_getconn (sv->connpool, 1);
+ FILE *outstrm = mcpc_conn_getstrm (outconn);
+ int pres = fprintf (outstrm, "%.*s", sv->rpcres.len, sv->rpcres.ptr);
+ mcpc_assert (pres >= 0, MCPC_EC_BUG);
+ fflush (outstrm);
+ sv->rpcres.len = 0;
+ }
+ else if (sv->typ == MCPC_SV_TCP)
+ {
+ mcpc_assert (0 < send (csock, sv->rpcres.ptr, sv->rpcres.len, 0), MCPC_EC_BUG);
+ sv->rpcres.len = 0;
+ }
+}
diff --git a/third_party/mcpc/src/server.h b/third_party/mcpc/src/server.h
new file mode 100644
index 0000000..e2c4141
--- /dev/null
+++ b/third_party/mcpc/src/server.h
@@ -0,0 +1,196 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h_server
+#define h_server
+
+#include <mcpc/visb.h>
+#include <mcpc/server.h>
+
+#include "ext_stdint.h"
+
+#include "errcode.h"
+#include "mjson.h"
+#include "rsc.h"
+#include "tool.h"
+#include "prmpt.h"
+
+#include <stdio.h>
+
+#if is_win
+#include <winsock2.h>
+typedef i32_t socklen_t;
+#endif
+
+#define BUFFER_SIZE 1024
+#define PORT 8080
+
+// ------------------------------ conn ------------------------------
+
+enum mcpc_conn_type
+{
+ MCPC_CONN_FD,
+ MCPC_CONN_STRM,
+};
+typedef enum mcpc_conn_type mcpc_conn_type_t;
+
+#if defined(is_unix)
+typedef int mcpc_sock_t;
+#elif is_win
+typedef SOCKET mcpc_sock_t;
+#endif
+
+static inline bool
+mcpc_sock_invalid (mcpc_sock_t sock)
+{
+#if defined(is_unix)
+ return sock < 0;
+#endif
+#if is_win
+ return sock == INVALID_SOCKET;
+#endif
+}
+
+enum mcpc_init_stat
+{
+ MCPC_INIT_NONE = 0,
+ MCPC_INIT_BEG,
+ MCPC_INIT_FAIL,
+ MCPC_INIT_SUCC,
+};
+typedef enum mcpc_init_stat mcpc_init_stat_t;
+
+typedef struct mcpc_conn mcpc_conn_t;
+struct mcpc_conn
+{
+ mcpc_conn_type_t typ;
+ union
+ {
+ mcpc_sock_t fd;
+ FILE *strm;
+ };
+ const ch8_t *cname;
+ u8_t cname_len;
+ mcpc_init_stat_t client_init;
+ mcpc_conn_t *nex;
+};
+
+typedef uint8_t mcpc_connpool_size_t;
+
+typedef struct mcpc_connpool mcpc_connpool_t;
+struct mcpc_connpool
+{
+ const mcpc_connpool_size_t cap;
+ mcpc_connpool_size_t len;
+ mcpc_conn_t *head;
+};
+
+// ----------------------------- server -----------------------------
+
+enum mcpc_server_type
+{
+ MCPC_SV_NONE = 0,
+ MCPC_SV_IOSTRM,
+ MCPC_SV_STDIO,
+ MCPC_SV_TCP,
+};
+typedef enum mcpc_server_type mcpc_server_type_t;
+
+struct mcpc_server
+{
+ mcpc_server_type_t typ;
+ const ch8_t *name;
+ u8_t name_len;
+ const ch8_t *ver;
+ u8_t ver_len;
+ u16_t capa;
+ const mcpc_toolpool_t *toolpool;
+ const mcpc_prmptpool_t *prmptpool;
+ const mcpc_rscpool_t *rscpool;
+ struct mjson_fixedbuf rpcres;
+ const mcpc_connpool_t *connpool;
+};
+// implication: for iostrm server, the first conn will be used as client init status storing
+// check mcpc_connpool_find_by_sock
+
+#define MCPC_CAPA_PRMT 0b0000000000000001
+#define MCPC_CAPA_PRMT_LISTCHG 0b0000000000000011
+#define MCPC_CAPA_TOOL 0b0000000000010000
+#define MCPC_CAPA_TOOL_LISTCHG 0b0000000000110000
+#define MCPC_CAPA_RSC 0b0000000100000000
+#define MCPC_CAPA_RSC_LISTCHG 0b0000001100000000
+#define MCPC_CAPA_RSC_SUBSRC 0b0000010100000000
+#define MCPC_CAPA_COMPLT 0b0001000000000000
+
+mcpc_server_t *mcpc_server_new (mcpc_server_type_t typ, mcpc_toolpool_t * toolpool, u32_t vargs_beg, ...);
+
+mcpc_server_t *mcpc_server_new_stdio ();
+
+/* void _handle_tools_list(mcpc_server_t *sv, struct jsonrpc_request *r); */
+
+/* void _handle_tools_call(mcpc_server_t *sv, struct jsonrpc_request *r); */
+
+mcpc_errcode_t mcpc_server_cleanup_rpcres (mcpc_server_t * sv);
+
+void handle_inone (mcpc_server_t * sv, const char *const rbuf, size_t nread, mcpc_sock_t csock);
+
+bool mcpc_server_supp_prompts (const mcpc_server_t * sv);
+
+bool mcpc_server_supp_prompts_lschg (const mcpc_server_t * sv);
+
+bool mcpc_server_supp_tools (const mcpc_server_t * sv);
+
+bool mcpc_server_supp_tools_lschg (const mcpc_server_t * sv);
+
+bool mcpc_server_supp_rscs (const mcpc_server_t * sv);
+
+bool mcpc_server_supp_rscs_lschg (const mcpc_server_t * sv);
+
+bool mcpc_server_supp_rscs_subs (const mcpc_server_t * sv);
+
+enum mcpc_metho
+{
+ MCPC_METHO_UNKNOWN = 0,
+ MCPC_METHO_INITBEG,
+ MCPC_METHO_INITDONE,
+ MCPC_METHO_LISTTOOL,
+ MCPC_METHO_CALLTOOL,
+ MCPC_METHO_LSRSC,
+ MCPC_METHO_PRMPT_LS,
+ MCPC_METHO_PRMPT_CALL,
+ MCPC_METHO_COMPLT_COMPLT,
+};
+typedef enum mcpc_metho mcpc_metho_t;
+
+// ---------------------------- connpool ----------------------------
+
+mcpc_conn_t *mcpc_conn_new (mcpc_conn_type_t typ, void *back);
+
+FILE *mcpc_conn_getstrm (mcpc_conn_t * conn);
+
+int mcpc_conn_getfd (mcpc_conn_t * conn);
+
+// TODO private
+mcpc_errcode_t mcpc_connpool_addref (mcpc_connpool_t * connpool, mcpc_conn_t * conn);
+
+mcpc_conn_t *mcpc_connpool_getconn (const mcpc_connpool_t * connpool, const mcpc_connpool_size_t idx);
+
+#endif
diff --git a/third_party/mcpc/src/tool.c b/third_party/mcpc/src/tool.c
new file mode 100644
index 0000000..7c746ec
--- /dev/null
+++ b/third_party/mcpc/src/tool.c
@@ -0,0 +1,505 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include "tool.h"
+
+#include "ext_string.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include "errcode.h"
+#include "alloc.h"
+#include "anydata.h"
+
+
+/* = = = = = = = = = = = = = = = toolprop = = = = = = = = = = = = = = = */
+
+MCPC_API mcpc_toolprop_t *
+mcpc_toolprop_new (const char8_t *name, const size_t name_len, const char8_t *desc, const size_t desc_len,
+ mcpc_anytype_t typ)
+{
+ mcpc_anydata_t *hp = mcpc_anydata_new_typ_meta1_meta2 (typ, name, name_len, desc, desc_len);
+
+ hp->flags |= ANYDATA_FL_REQUIRED;
+ hp->flags |= ANYDATA_FL_TOOLPROP;
+
+ return hp;
+}
+
+MCPC_API mcpc_toolprop_t *
+mcpc_toolprop_new2 (const char8_t *nament, const char8_t *descnt, mcpc_anytype_t typ)
+{
+ return mcpc_toolprop_new (nament, strlen ((const char *) nament), descnt, strlen ((const char *) descnt), typ);
+}
+
+MCPC_API bool
+mcpc_toolprop_is_required (const mcpc_toolprop_t *toolprop)
+{
+ return toolprop != nullptr && (toolprop->flags & ANYDATA_FL_REQUIRED) != 0;
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_toolprop_set_required (mcpc_toolprop_t *toolprop, bool required)
+{
+ if (toolprop == nullptr)
+ return MCPC_EC_BUG;
+ if (required)
+ toolprop->flags |= ANYDATA_FL_REQUIRED;
+ else
+ toolprop->flags &= ~ANYDATA_FL_REQUIRED;
+ return MCPC_EC_0;
+}
+
+void
+mcpc_toolprop_free (mcpc_toolprop_t *toolprop)
+{
+ mcpc_anydata_free ((mcpc_anydata_t *) toolprop);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_getref_name (const mcpc_toolprop_t *toolprop, const char8_t **ret, size_t *ret_len)
+{
+ return mcpc_anydata_getref_meta1 (toolprop, ret, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_getref_desc (const mcpc_toolprop_t *toolprop, const char8_t **ret, size_t *ret_len)
+{
+ return mcpc_anydata_getref_meta2 (toolprop, ret, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_get_data_type (const mcpc_toolprop_t *toolprop, mcpc_anytype_t *ret)
+{
+ bug_if_nullptr (toolprop);
+ bug_if_nullptr (ret);
+ *ret = ((const mcpc_anydata_t *) toolprop)->data_typ;
+ return MCPC_EC_0;
+}
+
+mcpc_errcode_t
+mcpc_toolprop_set_nament (mcpc_toolprop_t *toolprop, const char8_t *name)
+{
+ return mcpc_anydata_set_meta1nt (toolprop, name);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_set_i8 (mcpc_toolprop_t *toolprop, i8_t val)
+{
+ return mcpc_anydata_set_i8 (toolprop, val);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_set_u8 (mcpc_toolprop_t *toolprop, u8_t val)
+{
+ return mcpc_anydata_set_u8 (toolprop, val);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_set_i16 (mcpc_toolprop_t *toolprop, i16_t val)
+{
+ return mcpc_anydata_set_i16 (toolprop, val);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_set_u16 (mcpc_toolprop_t *toolprop, u16_t val)
+{
+ return mcpc_anydata_set_u16 (toolprop, val);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_set_i32 (mcpc_toolprop_t *toolprop, i32_t val)
+{
+ mcpc_errcode_t status = mcpc_anydata_set_i32 (toolprop, val);
+ if (status == MCPC_EC_0)
+ toolprop->flags |= ANYDATA_FL_PRESENT;
+ return status;
+}
+
+mcpc_errcode_t
+mcpc_toolprop_set_u32 (mcpc_toolprop_t *toolprop, u32_t val)
+{
+ return mcpc_anydata_set_u32 (toolprop, val);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_set_i64 (mcpc_toolprop_t *toolprop, i64_t val)
+{
+ return mcpc_anydata_set_i64 (toolprop, val);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_set_u64 (mcpc_toolprop_t *toolprop, u64_t val)
+{
+ return mcpc_anydata_set_u64 (toolprop, val);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_set_func (mcpc_toolprop_t *toolprop, void *val)
+{
+ return mcpc_anydata_set_func (toolprop, val);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_get_i8 (const mcpc_toolprop_t *toolprop, i8_t *ret)
+{
+ return mcpc_anydata_get_i8 (toolprop, ret);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_get_u8 (const mcpc_toolprop_t *toolprop, u8_t *ret)
+{
+ return mcpc_anydata_get_u8 (toolprop, ret);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_get_i16 (const mcpc_toolprop_t *toolprop, i16_t *ret)
+{
+ return mcpc_anydata_get_i16 (toolprop, ret);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_get_u16 (const mcpc_toolprop_t *toolprop, u16_t *ret)
+{
+ return mcpc_anydata_get_u16 (toolprop, ret);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_get_i32 (const mcpc_toolprop_t *toolprop, i32_t *ret)
+{
+ return mcpc_anydata_get_i32 (toolprop, ret);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_get_u32 (const mcpc_toolprop_t *toolprop, u32_t *ret)
+{
+ return mcpc_anydata_get_u32 (toolprop, ret);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_get_i64 (const mcpc_toolprop_t *toolprop, i64_t *ret)
+{
+ return mcpc_anydata_get_i64 (toolprop, ret);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_get_u64 (const mcpc_toolprop_t *toolprop, u64_t *ret)
+{
+ return mcpc_anydata_get_u64 (toolprop, ret);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_set_i8s (mcpc_toolprop_t *toolprop, const i8_t *arr, size_t arr_len)
+{
+ return mcpc_anydata_set_i8s (toolprop, arr, arr_len);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_set_u8s (mcpc_toolprop_t *toolprop, const u8_t *arr, size_t arr_len)
+{
+ return mcpc_anydata_set_u8s (toolprop, arr, arr_len);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_set_i16s (mcpc_toolprop_t *toolprop, const i16_t *arr, size_t arr_len)
+{
+ return mcpc_anydata_set_i16s (toolprop, arr, arr_len);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_set_u16s (mcpc_toolprop_t *toolprop, const u16_t *arr, size_t arr_len)
+{
+ return mcpc_anydata_set_u16s (toolprop, arr, arr_len);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_set_i32s (mcpc_toolprop_t *toolprop, const i32_t *arr, size_t arr_len)
+{
+ return mcpc_anydata_set_i32s (toolprop, arr, arr_len);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_set_u32s (mcpc_toolprop_t *toolprop, const u32_t *arr, size_t arr_len)
+{
+ return mcpc_anydata_set_u32s (toolprop, arr, arr_len);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_set_i64s (mcpc_toolprop_t *toolprop, const i64_t *arr, size_t arr_len)
+{
+ return mcpc_anydata_set_i64s (toolprop, arr, arr_len);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_set_u64s (mcpc_toolprop_t *toolprop, const u64_t *arr, size_t arr_len)
+{
+ return mcpc_anydata_set_u64s (toolprop, arr, arr_len);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_set_u8str (mcpc_toolprop_t *toolprop, const char8_t *arr, size_t arr_len)
+{
+ mcpc_errcode_t status = mcpc_anydata_set_u8str (toolprop, arr, arr_len);
+ if (status == MCPC_EC_0)
+ toolprop->flags |= ANYDATA_FL_PRESENT;
+ return status;
+}
+
+mcpc_errcode_t
+mcpc_toolprop_get_i8s (const mcpc_toolprop_t *toolprop, i8_t *ret, size_t ret_cap, size_t *ret_len)
+{
+ return mcpc_anydata_get_i8s (toolprop, ret, ret_cap, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_get_u8s (const mcpc_toolprop_t *toolprop, u8_t *ret, size_t ret_cap, size_t *ret_len)
+{
+ return mcpc_anydata_get_u8s (toolprop, ret, ret_cap, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_get_i16s (const mcpc_toolprop_t *toolprop, i16_t *ret, size_t ret_cap, size_t *ret_len)
+{
+ return mcpc_anydata_get_i16s (toolprop, ret, ret_cap, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_get_u16s (const mcpc_toolprop_t *toolprop, u16_t *ret, size_t ret_cap, size_t *ret_len)
+{
+ return mcpc_anydata_get_u16s (toolprop, ret, ret_cap, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_get_i32s (const mcpc_toolprop_t *toolprop, i32_t *ret, size_t ret_cap, size_t *ret_len)
+{
+ return mcpc_anydata_get_i32s (toolprop, ret, ret_cap, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_get_u32s (const mcpc_toolprop_t *toolprop, u32_t *ret, size_t ret_cap, size_t *ret_len)
+{
+ return mcpc_anydata_get_u32s (toolprop, ret, ret_cap, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_get_i64s (const mcpc_toolprop_t *toolprop, i64_t *ret, size_t ret_cap, size_t *ret_len)
+{
+ return mcpc_anydata_get_i64s (toolprop, ret, ret_cap, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_get_u64s (const mcpc_toolprop_t *toolprop, u64_t *ret, size_t ret_cap, size_t *ret_len)
+{
+ return mcpc_anydata_get_u64s (toolprop, ret, ret_cap, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_toolprop_get_u8str (const mcpc_toolprop_t *toolprop, char8_t *ret, size_t ret_cap, size_t *ret_len)
+{
+ return mcpc_anydata_get_u8str (toolprop, ret, ret_cap, ret_len);
+}
+
+/* = = = = = = = = = = = = = toolproppool = = = = = = = = = = = = = */
+
+size_t
+mcpc_toolproppool_get_len (const mcpc_toolproppool_t *toolproppool)
+{
+ bug_if_nullptr (toolproppool);
+ size_t ret = 0;
+ ret = ((const mcpc_anypool_t *) toolproppool)->len;
+ return ret;
+}
+
+const mcpc_toolprop_t *
+mcpc_toolproppool_get (const mcpc_toolproppool_t *toolproppool, size_t idx)
+{
+ bug_if_nullptr (toolproppool);
+ return ((mcpc_anypool_t *) toolproppool)->head + idx;
+}
+
+/* = = = = = = = = = = = = = = = = = tool = = = = = = = = = = = = = = = = = */
+
+MCPC_API mcpc_tool_t *
+mcpc_tool_new (const char8_t *name, const size_t name_len, const char8_t *desc, const size_t desc_len)
+{
+ mcpc_anydata_t *hp = mcpc_anydata_new_meta1_meta2 (name, name_len, desc, desc_len);
+
+ hp->flags |= ANYDATA_FL_TOOL;
+
+ return hp;
+}
+
+MCPC_API mcpc_tool_t *
+mcpc_tool_new2 (const char8_t *nament, const char8_t *descnt)
+{
+ return mcpc_tool_new (nament, strlen ((const char *) nament), descnt, strlen ((const char *) descnt));
+}
+
+void
+mcpc_tool_free (mcpc_tool_t *tool)
+{
+ mcpc_anydata_free ((mcpc_anydata_t *) tool);
+}
+
+mcpc_errcode_t
+mcpc_tool_getref_name (const mcpc_tool_t *tool, const char8_t **ret, size_t *ret_len)
+{
+ return mcpc_anydata_getref_meta1 (tool, ret, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_tool_getref_desc (const mcpc_tool_t *tool, const char8_t **ret, size_t *ret_len)
+{
+ return mcpc_anydata_getref_meta2 (tool, ret, ret_len);
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_tool_get_call_cb (const mcpc_tool_t *tool, mcpc_tcallcb_t *ret)
+{
+ return mcpc_anydata_get_func (tool, ret);
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_tool_set_call_cb (mcpc_tool_t *tool, mcpc_tcallcb_t cb)
+{
+ mcpc_anydata_set_func (tool, cb);
+ return MCPC_EC_0;
+}
+
+MCPC_API void
+mcpc_tool_addfre_toolprop (mcpc_tool_t *tool, mcpc_toolprop_t *toolprop)
+{
+ mcpc_anydata_add_child (tool, toolprop); // TODO return EC
+}
+
+MCPC_API void
+mcpc_tool_addcpy_toolprop (mcpc_tool_t *tool, mcpc_toolprop_t *toolprop)
+{
+ mcpc_anydata_addcpy_child (tool, toolprop); // TODO return EC
+}
+
+MCPC_API const mcpc_toolprop_t *
+mcpc_tool_get_toolprop (const mcpc_tool_t *tool, size_t idx)
+{
+ return mcpc_anydata_get_child (tool, idx);
+}
+
+size_t
+mcpc_tool_get_toolprop_len (const mcpc_tool_t *tool)
+{
+ bug_if_nullptr (tool);
+ mcpc_anydata_t *tool_imp = (mcpc_anydata_t *) tool;
+ if (tool_imp->chd_pool == nullptr)
+ return 0;
+ return tool_imp->chd_pool->len;
+}
+
+const mcpc_toolprop_t *
+mcpc_tool_getbynament_toolprop (const mcpc_tool_t *tool, const char8_t *tprop_nament)
+{
+ const mcpc_anydata_t *chd = mcpc_anypool_getbymeta1nt (tool->chd_pool, tprop_nament);
+ return (mcpc_toolprop_t *) chd;
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_tool_get_tpropval_i32 (const mcpc_tool_t *tool, const char8_t *tprop_nament, i32_t *ret)
+{
+ bug_if_nullptr (tool);
+ bug_if_nullptr (tprop_nament);
+ bug_if_nullptr (ret);
+ const mcpc_toolprop_t *chd = mcpc_tool_getbynament_toolprop (tool, tprop_nament);
+ if (chd == nullptr)
+ return MCPC_EC_TOOLPROP_NOTFOUND;
+ if ((chd->flags & ANYDATA_FL_PRESENT) == 0)
+ return MCPC_EC_ANYDATA_NOTFOUND;
+
+ return mcpc_toolprop_get_i32 (chd, ret);
+}
+
+MCPC_API mcpc_errcode_t
+mcpc_tool_get_tpropval_u8str (const mcpc_tool_t *tool, const char8_t *tprop_nament, char8_t *ret,
+ size_t ret_cap, size_t *ret_len)
+{
+ bug_if_nullptr (tool);
+ bug_if_nullptr (tprop_nament);
+ bug_if_nullptr (ret);
+ const mcpc_toolprop_t *chd = mcpc_tool_getbynament_toolprop (tool, tprop_nament);
+ if (chd == nullptr)
+ return MCPC_EC_TOOLPROP_NOTFOUND;
+ if ((chd->flags & ANYDATA_FL_PRESENT) == 0)
+ return MCPC_EC_ANYDATA_NOTFOUND;
+
+ return mcpc_anydata_get_u8str (chd, ret, ret_cap, ret_len);
+}
+
+const mcpc_toolproppool_t *
+mcpc_tool_get_toolproppool (const mcpc_tool_t *tool)
+{
+ return tool->chd_pool;
+}
+
+/* = = = = = = = = = = = = = = = toolpool = = = = = = = = = = = = = = = */
+
+mcpc_toolpool_t *
+mcpc_toolpool_new ()
+{
+ return mcpc_anypool_new ();
+}
+
+void
+mcpc_toolpool_free (mcpc_toolpool_t *toolpool)
+{
+ mcpc_anypool_free (toolpool);
+}
+
+void
+mcpc_toolpool_addcpy (mcpc_toolpool_t *toolpool, const mcpc_tool_t *tool)
+{
+ mcpc_anypool_addcpy (toolpool, tool);
+}
+
+void
+mcpc_toolpool_addfre (mcpc_toolpool_t *toolpool, mcpc_tool_t *tool)
+{
+ mcpc_anypool_addfre (toolpool, tool);
+}
+
+size_t
+mcpc_toolpool_get_len (const mcpc_toolpool_t *toolpool)
+{
+ bug_if_nullptr (toolpool);
+ size_t ret = 0;
+ ret = ((const mcpc_anypool_t *) toolpool)->len;
+ return ret;
+}
+
+const mcpc_tool_t *
+mcpc_toolpool_get (const mcpc_toolpool_t *toolpool, size_t idx)
+{
+ return mcpc_anypool_get (toolpool, idx);
+}
+
+const mcpc_tool_t *
+mcpc_toolpool_getbyname (const mcpc_toolpool_t *toolpool, const char8_t *name, size_t name_len)
+{
+ return mcpc_anypool_getbymeta1 (toolpool, name, name_len);
+}
diff --git a/third_party/mcpc/src/tool.h b/third_party/mcpc/src/tool.h
new file mode 100644
index 0000000..7da9bcd
--- /dev/null
+++ b/third_party/mcpc/src/tool.h
@@ -0,0 +1,150 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h_tool
+#define h_tool
+
+#include <mcpc/tool.h>
+
+#include "ext_stdint.h"
+
+typedef mcpc_anypool_t mcpc_toolpool_t;
+typedef mcpc_anypool_t mcpc_toolproppool_t;
+
+// ---------------------------- toolprop ----------------------------
+
+void mcpc_toolprop_free (mcpc_toolprop_t * toolprop);
+
+mcpc_errcode_t mcpc_toolprop_getref_name (const mcpc_toolprop_t * toolprop, const char8_t ** ret, size_t *ret_len);
+
+mcpc_errcode_t mcpc_toolprop_getref_desc (const mcpc_toolprop_t * toolprop, const char8_t ** ret, size_t *ret_len);
+
+mcpc_errcode_t mcpc_toolprop_get_data_type (const mcpc_toolprop_t * toolprop, mcpc_anytype_t * ret);
+
+mcpc_errcode_t mcpc_toolprop_set_nament (mcpc_toolprop_t * toolprop, const char8_t * name);
+
+mcpc_errcode_t mcpc_toolprop_set_i8 (mcpc_toolprop_t * toolprop, i8_t val);
+
+mcpc_errcode_t mcpc_toolprop_set_u8 (mcpc_toolprop_t * toolprop, u8_t val);
+
+mcpc_errcode_t mcpc_toolprop_set_i16 (mcpc_toolprop_t * toolprop, i16_t val);
+
+mcpc_errcode_t mcpc_toolprop_set_u16 (mcpc_toolprop_t * toolprop, u16_t val);
+
+mcpc_errcode_t mcpc_toolprop_set_i32 (mcpc_toolprop_t * toolprop, i32_t val);
+
+mcpc_errcode_t mcpc_toolprop_set_u32 (mcpc_toolprop_t * toolprop, u32_t val);
+
+mcpc_errcode_t mcpc_toolprop_set_i64 (mcpc_toolprop_t * toolprop, i64_t val);
+
+mcpc_errcode_t mcpc_toolprop_set_u64 (mcpc_toolprop_t * toolprop, u64_t val);
+
+mcpc_errcode_t mcpc_toolprop_set_func (mcpc_toolprop_t * toolprop, void *val);
+
+mcpc_errcode_t mcpc_toolprop_get_i8 (const mcpc_toolprop_t * toolprop, i8_t * ret);
+
+mcpc_errcode_t mcpc_toolprop_get_u8 (const mcpc_toolprop_t * toolprop, u8_t * ret);
+
+mcpc_errcode_t mcpc_toolprop_get_i16 (const mcpc_toolprop_t * toolprop, i16_t * ret);
+
+mcpc_errcode_t mcpc_toolprop_get_u16 (const mcpc_toolprop_t * toolprop, u16_t * ret);
+
+mcpc_errcode_t mcpc_toolprop_get_i32 (const mcpc_toolprop_t * toolprop, i32_t * ret);
+
+mcpc_errcode_t mcpc_toolprop_get_u32 (const mcpc_toolprop_t * toolprop, u32_t * ret);
+
+mcpc_errcode_t mcpc_toolprop_get_i64 (const mcpc_toolprop_t * toolprop, i64_t * ret);
+
+mcpc_errcode_t mcpc_toolprop_get_u64 (const mcpc_toolprop_t * toolprop, u64_t * ret);
+
+mcpc_errcode_t mcpc_toolprop_get_func (const mcpc_toolprop_t * toolprop, void *ret);
+
+mcpc_errcode_t mcpc_toolprop_set_i8s (mcpc_toolprop_t * toolprop, const i8_t * arr, size_t arr_len);
+
+mcpc_errcode_t mcpc_toolprop_set_u8s (mcpc_toolprop_t * toolprop, const u8_t * arr, size_t arr_len);
+
+mcpc_errcode_t mcpc_toolprop_set_i16s (mcpc_toolprop_t * toolprop, const i16_t * arr, size_t arr_len);
+
+mcpc_errcode_t mcpc_toolprop_set_u16s (mcpc_toolprop_t * toolprop, const u16_t * arr, size_t arr_len);
+
+mcpc_errcode_t mcpc_toolprop_set_i32s (mcpc_toolprop_t * toolprop, const i32_t * arr, size_t arr_len);
+
+mcpc_errcode_t mcpc_toolprop_set_u32s (mcpc_toolprop_t * toolprop, const u32_t * arr, size_t arr_len);
+
+mcpc_errcode_t mcpc_toolprop_set_i64s (mcpc_toolprop_t * toolprop, const i64_t * arr, size_t arr_len);
+
+mcpc_errcode_t mcpc_toolprop_set_u64s (mcpc_toolprop_t * toolprop, const u64_t * arr, size_t arr_len);
+
+mcpc_errcode_t mcpc_toolprop_set_u8str (mcpc_toolprop_t * toolprop, const char8_t * arr, size_t arr_len);
+
+mcpc_errcode_t mcpc_toolprop_get_i8s (const mcpc_toolprop_t * toolprop, i8_t * ret, size_t ret_cap, size_t *ret_len);
+
+mcpc_errcode_t mcpc_toolprop_get_u8s (const mcpc_toolprop_t * toolprop, u8_t * ret, size_t ret_cap, size_t *ret_len);
+
+mcpc_errcode_t mcpc_toolprop_get_i16s (const mcpc_toolprop_t * toolprop, i16_t * ret, size_t ret_cap, size_t *ret_len);
+
+mcpc_errcode_t mcpc_toolprop_get_u16s (const mcpc_toolprop_t * toolprop, u16_t * ret, size_t ret_cap, size_t *ret_len);
+
+mcpc_errcode_t mcpc_toolprop_get_i32s (const mcpc_toolprop_t * toolprop, i32_t * ret, size_t ret_cap, size_t *ret_len);
+
+mcpc_errcode_t mcpc_toolprop_get_u32s (const mcpc_toolprop_t * toolprop, u32_t * ret, size_t ret_cap, size_t *ret_len);
+
+mcpc_errcode_t mcpc_toolprop_get_i64s (const mcpc_toolprop_t * toolprop, i64_t * ret, size_t ret_cap, size_t *ret_len);
+
+mcpc_errcode_t mcpc_toolprop_get_u64s (const mcpc_toolprop_t * toolprop, u64_t * ret, size_t ret_cap, size_t *ret_len);
+
+mcpc_errcode_t mcpc_toolprop_get_u8str (const mcpc_toolprop_t * toolprop, char8_t * ret, size_t ret_cap,
+ size_t *ret_len);
+
+const mcpc_toolproppool_t *mcpc_tool_get_toolproppool (const mcpc_tool_t * tool);
+
+// -------------------------- toolproppool --------------------------
+
+size_t mcpc_toolproppool_get_len (const mcpc_toolproppool_t * toolproppool);
+
+const mcpc_toolprop_t *mcpc_toolproppool_get (const mcpc_toolproppool_t * toolproppool, size_t idx);
+
+// ------------------------------ tool ------------------------------
+
+mcpc_errcode_t mcpc_tool_getref_name (const mcpc_tool_t * tool, const char8_t ** ret, size_t *ret_len);
+
+mcpc_errcode_t mcpc_tool_getref_desc (const mcpc_tool_t * tool, const char8_t ** ret, size_t *ret_len);
+
+void mcpc_tool_free (mcpc_tool_t * tool);
+
+// ---------------------------- toolpool ----------------------------
+
+mcpc_toolpool_t *mcpc_toolpool_new ();
+
+void mcpc_toolpool_free (mcpc_toolpool_t * toolpool);
+
+void mcpc_toolpool_addcpy (mcpc_toolpool_t * toolpool, const mcpc_tool_t * tool);
+
+void mcpc_toolpool_addfre (mcpc_toolpool_t * toolpool, mcpc_tool_t * tool);
+
+size_t mcpc_toolpool_get_len (const mcpc_toolpool_t * toolpool);
+
+const mcpc_tool_t *mcpc_toolpool_get (const mcpc_toolpool_t * toolpool, size_t idx);
+
+const mcpc_tool_t *mcpc_toolpool_getbyname (const mcpc_toolpool_t * toolpool, const char8_t * name, size_t name_len);
+
+
+#endif
diff --git a/third_party/mcpc/src/ucbr.c b/third_party/mcpc/src/ucbr.c
new file mode 100644
index 0000000..5dfce26
--- /dev/null
+++ b/third_party/mcpc/src/ucbr.c
@@ -0,0 +1,288 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include "ucbr.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "alloc.h"
+#include "anydata.h"
+#include "errcode.h"
+#include "ext_string.h"
+
+/* = = = = = = = = = = = = = = = = ucbrchd = = = = = = = = = = = = = = = = */
+
+mcpc_ucbrchd_t *
+mcpc_ucbrchd_new_vprintf8 (const char8_t *fmt, va_list *vargs)
+{
+ size_t sbuf_cap = 1024;
+ size_t sbuf_len = 0;
+ char8_t *sbuf = mcpc_alloc (sbuf_cap);
+ int printed = 0;
+ while (true)
+ {
+ va_list vargs_cp;
+ va_copy (vargs_cp, *vargs);
+ printed
+ = vsnprintf ((char *)sbuf, sbuf_cap, (const char *)fmt, vargs_cp);
+ bug_if (printed < 0);
+ if ((size_t)printed >= sbuf_cap)
+ {
+ sbuf_cap += sbuf_cap;
+ sbuf = mcpc_realloc (sbuf, sbuf_cap);
+ }
+ else
+ {
+ sbuf_len = (size_t)printed;
+ break;
+ }
+ }
+
+ mcpc_anydata_t *anydata = mcpc_anydata_new_typ (MCPC_U8STR);
+
+ if (MCPC_EC_0 != mcpc_anydata_set_u8str (anydata, sbuf, sbuf_len))
+ goto badret;
+
+ free (sbuf);
+ return (mcpc_ucbrchd_t *)anydata;
+
+badret:
+ free (sbuf);
+ mcpc_anydata_free (anydata);
+ return nullptr;
+}
+
+mcpc_errcode_t
+mcpc_ucbrchd_getref_u8str (const mcpc_ucbrchd_t *ucbrchd, const char8_t **ret,
+ size_t *ret_len)
+{
+ return mcpc_anydata_getref_u8str (ucbrchd, ret, ret_len);
+}
+
+mcpc_errcode_t
+mcpc_ucbrchd_get_data_type (const mcpc_ucbrchd_t *ucbrchd, mcpc_anytype_t *ret)
+{
+ bug_if_nullptr (ucbrchd);
+ bug_if_nullptr (ret);
+
+ *ret = ucbrchd->data_typ;
+
+ return MCPC_EC_0;
+}
+
+bool
+mcpc_ucbrchd_is_tool_callres (const mcpc_ucbrchd_t *ucbrchd)
+{
+ return ucbrchd->flags & ANYDATA_FL_USRCBRES_TOOLCALL;
+}
+
+bool
+mcpc_ucbrchd_is_prmpt_callres (const mcpc_ucbrchd_t *ucbrchd)
+{
+ return ucbrchd->flags & ANYDATA_FL_USRCBRES_PRMPTCALL;
+}
+
+bool
+mcpc_ucbrchd_is_assist_prmpt (const mcpc_ucbrchd_t *ucbrchd)
+{
+ return ucbrchd->flags & ANYDATA_FL_USRCBRES_PRMPTCALL_ASSIST;
+}
+
+/* = = = = = = = = = = = = = = = = = ucbr = = = = = = = = = = = = = = = = = */
+
+mcpc_ucbr_t *
+mcpc_ucbr_new ()
+{
+ return mcpc_anypool_new ();
+}
+
+void
+mcpc_ucbr_free (mcpc_ucbr_t *ucbr)
+{
+ mcpc_anypool_free (ucbr);
+}
+
+void
+mcpc_ucbr_addcpy (mcpc_ucbr_t *ucbr, const mcpc_ucbrchd_t *ucbrchd)
+{
+ mcpc_anypool_addcpy (ucbr, ucbrchd);
+}
+
+void
+mcpc_ucbr_addfre (mcpc_ucbr_t *ucbr, mcpc_ucbrchd_t *ucbrchd)
+{
+ mcpc_anypool_addfre (ucbr, ucbrchd);
+}
+
+bool
+mcpc_ucbr_is_err (const mcpc_ucbr_t *ucbr)
+{
+ bug_if_nullptr (ucbr);
+ return ucbr->flags & ANYPOOL_FL_USRCBRES_TOOLCALL_ERROR;
+}
+
+size_t
+mcpc_ucbr_get_len (const mcpc_ucbr_t *ucbr)
+{
+ bug_if_nullptr (ucbr);
+ size_t ret = 0;
+ ret = ((const mcpc_anypool_t *)ucbr)->len;
+ return ret;
+}
+
+const mcpc_ucbrchd_t *
+mcpc_ucbr_get (const mcpc_ucbr_t *ucbr, size_t idx)
+{
+ return mcpc_anypool_get (ucbr, idx);
+}
+
+const mcpc_ucbrchd_t *
+mcpc_ucbr_getbyname (const mcpc_ucbr_t *ucbr, const char8_t *name,
+ size_t name_len)
+{
+ return mcpc_anypool_getbymeta1 (ucbr, name, name_len);
+}
+
+const mcpc_ucbrchd_t *
+mcpc_ucbr_get_next (const mcpc_ucbr_t *ucbr)
+{
+ bug_if_nullptr (ucbr);
+
+ static size_t idx = 0;
+
+ const mcpc_anypool_t *pool = ucbr;
+ if (pool == nullptr)
+ return nullptr;
+
+ const size_t pool_len = pool->len;
+ if (pool_len == 0)
+ return nullptr;
+
+ if (idx == pool_len) // TODO MT-SAFE
+ {
+ idx = 0;
+ return nullptr;
+ }
+
+ const mcpc_anydata_t *ret = pool->head + idx;
+ idx += 1;
+
+ return ret;
+}
+
+size_t
+mcpc_ucbr_get_serlz_lenhint (const mcpc_ucbr_t *ucbr)
+{
+ bug_if_nullptr (ucbr);
+ size_t ret = 0;
+
+ size_t length = mcpc_ucbr_get_len (ucbr);
+ for (size_t i = 0; i < length; ++i)
+ {
+ const mcpc_ucbrchd_t *cur_chd = mcpc_ucbr_get (ucbr, i);
+ ret += cur_chd->data_len;
+ }
+
+ /* JSON string escaping can expand one input byte to six output bytes. */
+ if (ret > (SIZE_MAX - 1024) / 6)
+ return 0;
+ ret = ret * 6 + 1024;
+
+ return ret;
+}
+
+void
+mcpc_ucbr_toolcallres_add_text_vprintf8 (mcpc_ucbr_t *ucbr, const char8_t *fmt,
+ va_list *vargs)
+{
+ bug_if_nullptr (ucbr);
+ bug_if_nullptr (fmt);
+ bug_if_nullptr (vargs);
+
+ ucbr->flags |= ANYPOOL_FL_USRCBRES_TOOLCALL;
+
+ mcpc_ucbrchd_t *newctn = mcpc_ucbrchd_new_vprintf8 (fmt, vargs);
+ newctn->flags |= ANYDATA_FL_USRCBRES_TOOLCALL;
+ newctn->flags |= ANYDATA_FL_USRCBRES_TOOLCALL_TXTCTN;
+ mcpc_ucbr_addfre (ucbr, newctn);
+}
+
+MCPC_API void
+mcpc_ucbr_toolcall_add_text_printf8 (mcpc_ucbr_t *ucbr, const char8_t *fmt,
+ ...)
+{
+ va_list vargs;
+ va_start (vargs, fmt);
+
+ mcpc_ucbr_toolcallres_add_text_vprintf8 (ucbr, fmt, &vargs);
+
+ va_end (vargs);
+}
+
+MCPC_API void
+mcpc_ucbr_toolcall_add_errmsg_printf8 (mcpc_ucbr_t *ucbr, const char8_t *fmt,
+ ...)
+{
+ va_list vargs;
+ va_start (vargs, fmt);
+
+ ucbr->flags |= ANYPOOL_FL_USRCBRES_TOOLCALL_ERROR;
+ mcpc_ucbr_toolcallres_add_text_vprintf8 (ucbr, fmt, &vargs);
+
+ va_end (vargs);
+}
+
+MCPC_API void
+mcpc_ucbr_prmptget_add_user_printf8 (mcpc_ucbr_t *ucbr, const char8_t *fmt,
+ ...)
+{
+ va_list vargs;
+ va_start (vargs, fmt);
+
+ ucbr->flags |= ANYPOOL_FL_USRCBRES_PRMPTCALL;
+
+ mcpc_ucbrchd_t *newctn = mcpc_ucbrchd_new_vprintf8 (fmt, &vargs);
+ newctn->flags |= ANYDATA_FL_USRCBRES_PRMPTCALL;
+
+ mcpc_ucbr_addfre (ucbr, newctn);
+
+ va_end (vargs);
+}
+
+MCPC_API void
+mcpc_ucbr_prmptget_add_assist_printf8 (mcpc_ucbr_t *ucbr, const char8_t *fmt,
+ ...)
+{
+ va_list vargs;
+ va_start (vargs, fmt);
+
+ ucbr->flags |= ANYPOOL_FL_USRCBRES_PRMPTCALL;
+
+ mcpc_ucbrchd_t *newctn = mcpc_ucbrchd_new_vprintf8 (fmt, &vargs);
+ newctn->flags |= ANYDATA_FL_USRCBRES_PRMPTCALL;
+ newctn->flags |= ANYDATA_FL_USRCBRES_PRMPTCALL_ASSIST;
+
+ mcpc_ucbr_addfre (ucbr, newctn);
+
+ va_end (vargs);
+}
diff --git a/third_party/mcpc/src/ucbr.h b/third_party/mcpc/src/ucbr.h
new file mode 100644
index 0000000..796688d
--- /dev/null
+++ b/third_party/mcpc/src/ucbr.h
@@ -0,0 +1,61 @@
+// This file is part of the mcpc library.
+//
+// Copyright (C) 2025 Michael Lee <micl2e2 AT proton.me>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef h_ucbr
+#define h_ucbr
+
+#include <stddef.h>
+
+#include <mcpc/ucbr.h>
+
+typedef mcpc_anydata_t mcpc_ucbrchd_t;
+
+bool mcpc_ucbrchd_is_tool_callres (const mcpc_ucbrchd_t * ucbrchd);
+
+bool mcpc_ucbrchd_is_prmpt_callres (const mcpc_ucbrchd_t * ucbrchd);
+
+bool mcpc_ucbrchd_is_assist_prmpt (const mcpc_ucbrchd_t * ucbrchd);
+
+mcpc_errcode_t mcpc_ucbrchd_getref_u8str (const mcpc_ucbrchd_t * ucbrchd, const char8_t ** ret, size_t *ret_len);
+
+mcpc_errcode_t mcpc_ucbrchd_get_data_type (const mcpc_ucbrchd_t * ucbrchd, mcpc_anytype_t * ret);
+
+// ------------------------------ ucbr ------------------------------
+
+mcpc_ucbr_t *mcpc_ucbr_new ();
+
+void mcpc_ucbr_free (mcpc_ucbr_t * ucbr);
+
+size_t mcpc_ucbr_get_len (const mcpc_ucbr_t * ucbr);
+
+const mcpc_ucbrchd_t *mcpc_ucbr_get (const mcpc_ucbr_t * ucbr, size_t idx);
+
+const mcpc_ucbrchd_t *mcpc_ucbr_get_next (const mcpc_ucbr_t * ucbr);
+
+size_t mcpc_ucbr_get_serlz_lenhint (const mcpc_ucbr_t * ucbr);
+
+void mcpc_ucbr_addfre (mcpc_ucbr_t * ucbr, mcpc_ucbrchd_t * ucbrchd);
+
+bool mcpc_ucbr_is_err (const mcpc_ucbr_t * ucbr);
+
+
+
+#endif
diff --git a/third_party/mcpc/src/ver.txt b/third_party/mcpc/src/ver.txt
new file mode 100644
index 0000000..6c6aa7c
--- /dev/null
+++ b/third_party/mcpc/src/ver.txt
@@ -0,0 +1 @@
+0.1.0 \ No newline at end of file
diff --git a/third_party/mcpc/tst/.indent.pro b/third_party/mcpc/tst/.indent.pro
new file mode 100644
index 0000000..f7bb493
--- /dev/null
+++ b/third_party/mcpc/tst/.indent.pro
@@ -0,0 +1 @@
+../.indent.pro \ No newline at end of file
diff --git a/third_party/mcpc/tst/CMakeLists.txt b/third_party/mcpc/tst/CMakeLists.txt
new file mode 100644
index 0000000..d9b5bd5
--- /dev/null
+++ b/third_party/mcpc/tst/CMakeLists.txt
@@ -0,0 +1,33 @@
+
+
+add_compile_options(/Dis_win)
+add_compile_options(/Dis_wincl)
+add_compile_options(/nologo)
+add_compile_options(/std:c17)
+add_compile_options(/DMCPC_C23PTCH_KW1)
+add_compile_options(/DMCPC_C23PTCH_CKD1)
+add_compile_options(/DMCPC_C23PTCH_UCHAR1)
+
+
+# --------------------------- Add A Test ---------------------------
+
+set (CurrTestName server1)
+
+add_executable (${CurrTestName} ${CurrTestName}.c commtst.c)
+
+target_include_directories (
+ ${CurrTestName}
+ PRIVATE ${PROJECT_SOURCE_DIR}
+ PRIVATE ${PROJECT_SOURCE_DIR}/src
+)
+
+target_link_libraries (${CurrTestName} ws2_32)
+target_link_libraries (${CurrTestName} mcpc)
+
+add_test(NAME ${CurrTestName} COMMAND ${CurrTestName})
+
+configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/server1-req-initialize-1.json
+ ${CMAKE_CURRENT_BINARY_DIR}/server1-req-initialize-1.json
+ COPYONLY
+)
diff --git a/third_party/mcpc/tst/Makefile b/third_party/mcpc/tst/Makefile
new file mode 100644
index 0000000..5951716
--- /dev/null
+++ b/third_party/mcpc/tst/Makefile
@@ -0,0 +1,108 @@
+include ../mk/step0.mk
+
+ifneq (,$(is_linux))
+ include ../mk/ccdefs-linux.mk
+endif
+ifneq (,$(is_cygwin))
+ include ../mk/ccdefs-cygwin.mk
+endif
+ifneq (,$(is_mingw))
+ include ../mk/ccdefs-mingw.mk
+endif
+ifneq (,$(is_macos))
+ include ../mk/ccdefs-macos.mk
+endif
+ifneq (,$(is_win))
+ include ../mk/ccdefs-win.mk
+endif
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+include ../mk/dbgrel.mk
+
+
+ifneq (,$(is_gcclike))
+ CFLAGS += -I../src
+endif
+ifneq (,$(is_cllike))
+ CFLAGS += /I..\src
+endif
+
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+default: link utst
+
+clean:
+ $(if $(is_cmd) , del *.pdb *.ilk *.o *.out *.a *.so *.dSYM *.obj *.exe *.lib *.dll *.exp *.log )
+ $(if $(is_sh) , rm -rf *.pdb *.ilk *.o *.out *.a *.so *.dSYM *.obj *.exe *.lib *.dll *.exp *.log )
+
+comp: itst-svcalc_COMP
+
+link: itst-svcalc_LINKE
+
+utst: itst-svcalc_EXEC
+
+itst: itst-svcalc_EXEC
+
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# itst-svcalc
+
+itst-svcalc_srcs =
+itst-svcalc_srcs += commtst.c
+itst-svcalc_srcs += itst-svcalc.c
+
+itst-svcalc_objs =
+ifneq (,$(is_gcclike))
+ itst-svcalc_objs = $(patsubst %.c,%.o,$(itst-svcalc_srcs))
+else ifneq (,$(is_cllike))
+ itst-svcalc_objs = $(patsubst %.c,%.obj,$(itst-svcalc_srcs))
+endif
+
+itst-svcalc_ldflags =
+ifneq (,$(is_unixar))
+ itst-svcalc_ldflags += ../src/libmcpc.a
+endif
+ifneq (,$(is_manual_pthread))
+ itst-svcalc_ldflags += -lpthread
+endif
+ifneq (,$(is_winlib))
+ itst-svcalc_ldflags += ws2_32.lib
+ itst-svcalc_ldflags += ..\src\mcpc.lib
+endif
+ifneq (,$(is_winld))
+ itst-svcalc_ldflags += -lws2_32
+endif
+
+
+itst-svcalc_COMP:
+ $(if $(is_gcclike) , $(CC) $(CFLAGS) $(itst-svcalc_srcs) -c )
+ $(if $(is_cllike) , $(CC) $(CFLAGS) $(itst-svcalc_srcs) /c )
+itst-svcalc_LINKE: itst-svcalc_COMP
+ $(if $(is_gcclike) , $(CC) $(itst-svcalc_objs) $(LDFLAGS) $(itst-svcalc_ldflags) -o itst-svcalc.out )
+ $(if $(is_cllike) , $(CC) $(itst-svcalc_objs) /link /out:itst-svcalc.exe $(LDFLAGS) $(itst-svcalc_ldflags))
+itst-svcalc_EXEC: itst-svcalc_LINKE
+ @ echo>>$@.stdout.log; echo>>$@.stderr.log;
+ $(if $(is_unix) , @ date>>$@.stdout.log; date>>$@.stderr.log; )
+ $(if $(is_cmd) , echo ___>>$@.stdout.log; echo ___>>$@.stderr.log; )
+ $(if $(is_pkill) , pkill itst-sv || printf "" )
+ # itst-svcalc toolslist
+ $(if $(is_dirsh) , ./itst-svcalc.out iostrm -i itst-svcalc-req-toolslist-1.json 1>>$@.stdout.log 2>>$@.stderr.log )
+ $(if $(is_cmd) , itst-svcalc.exe iostrm -i itst-svcalc-req-toolslist-1.json 1>>$@.stdout.log 2>>$@.stderr.log )
+ $(if $(and $(is_indirsh),$(is_cllike)) , ./itst-svcalc.exe iostrm -i itst-svcalc-req-toolslist-1.json 1>>$@.stdout.log 2>>$@.stderr.log )
+ $(if $(and $(is_indirsh),$(is_gcclike)) , ./itst-svcalc.out iostrm -i itst-svcalc-req-toolslist-1.json 1>>$@.stdout.log 2>>$@.stderr.log )
+ $(if $(is_valg) , valgrind --leak-check=full --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=222 ./itst-svcalc.out iostrm -i itst-svcalc-req-toolslist-1.json 1>/dev/null 2>/dev/null )
+ # itst-svcalc promptslist
+ $(if $(is_dirsh) , ./itst-svcalc.out iostrm -i itst-svcalc-req-promptslist-1.json 1>>$@.stdout.log 2>>$@.stderr.log )
+ $(if $(is_cmd) , itst-svcalc.exe iostrm -i itst-svcalc-req-promptslist-1.json 1>>$@.stdout.log 2>>$@.stderr.log )
+ $(if $(and $(is_indirsh),$(is_cllike)) , ./itst-svcalc.exe iostrm -i itst-svcalc-req-promptslist-1.json 1>>$@.stdout.log 2>>$@.stderr.log )
+ $(if $(and $(is_indirsh),$(is_gcclike)) , ./itst-svcalc.out iostrm -i itst-svcalc-req-promptslist-1.json 1>>$@.stdout.log 2>>$@.stderr.log )
+ $(if $(is_valg) , valgrind --leak-check=full --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=222 ./itst-svcalc.out iostrm -i itst-svcalc-req-promptslist-1.json 1>/dev/null 2>/dev/null )
+ # itst-svcalc resourceslist
+ $(if $(is_dirsh) , ./itst-svcalc.out iostrm -i itst-svcalc-req-resourceslist-1.json 1>>$@.stdout.log 2>>$@.stderr.log )
+ $(if $(is_cmd) , itst-svcalc.exe iostrm -i itst-svcalc-req-resourceslist-1.json 1>>$@.stdout.log 2>>$@.stderr.log )
+ $(if $(and $(is_indirsh),$(is_cllike)) , ./itst-svcalc.exe iostrm -i itst-svcalc-req-resourceslist-1.json 1>>$@.stdout.log 2>>$@.stderr.log )
+ $(if $(and $(is_indirsh),$(is_gcclike)) , ./itst-svcalc.out iostrm -i itst-svcalc-req-resourceslist-1.json 1>>$@.stdout.log 2>>$@.stderr.log )
+ $(if $(is_valg) , valgrind --leak-check=full --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=222 ./itst-svcalc.out iostrm -i itst-svcalc-req-resourceslist-1.json 1>/dev/null 2>/dev/null )
+ @echo P A S S E D : $(patsubst %_EXEC,%,$@)
diff --git a/third_party/mcpc/tst/commtst.c b/third_party/mcpc/tst/commtst.c
new file mode 100644
index 0000000..a2ba8dc
--- /dev/null
+++ b/third_party/mcpc/tst/commtst.c
@@ -0,0 +1,181 @@
+#include "commtst.h"
+
+#include <stdint.h>
+
+i32_t
+fsize (FILE *strm)
+{
+ fseek (strm, 0, SEEK_SET);
+ fseek (strm, 0, SEEK_END);
+ long sz = ftell (strm);
+ assert (sz > 0);
+ fseek (strm, 0, SEEK_SET);
+ return sz;
+}
+
+void
+read_atmost (void *src, char *rbuf, const size_t nmost, size_t *nread)
+{
+#ifdef is_win
+ HANDLE *hdlp = (HANDLE *) src;
+#else
+ int *fdp = (int *) src;
+#endif
+ size_t nread_accm = 0;
+ size_t nleft = nmost;
+ while (nleft > 0)
+ {
+ ssize_t nbuffered = nmost - nleft;
+#ifdef is_win
+ ssize_t nread_cur = 0;
+ assT (ReadFile
+ (*hdlp, rbuf + nbuffered, (DWORD)nleft, (LPDWORD) (&nread_cur),
+ nullptr));
+#else
+ ssize_t nread_cur = read (*fdp, rbuf + nbuffered, nleft);
+#endif
+ if (nread_cur <= 0)
+ break;
+ nread_accm += nread_cur;
+ nleft -= nread_cur;
+ }
+ *nread = nread_accm;
+}
+
+void
+recv_atmost (
+#if is_unix
+ int csock,
+#elif is_win
+ SOCKET csock,
+#endif
+ char *rbuf, const size_t nmost, size_t *nread)
+{
+ size_t nread_accm = 0;
+ size_t nleft = nmost;
+ while (nleft > 0)
+ {
+ ssize_t nbuffered = nmost - nleft;
+ ssize_t nread_cur = recv (csock, rbuf + nbuffered, (int)nleft, 0);
+ if (nread_cur <= 0)
+ break;
+ nread_accm += nread_cur;
+ nleft -= nread_cur;
+ }
+ *nread = nread_accm;
+}
+
+i32_t
+read_u8str (const char8_t *const filename, char8_t *const destbuf)
+{
+ FILE *file;
+
+#if is_win
+ fopen_s (&file, (const char*) filename, "rb,ccs=UTF-8");
+#else
+ file = fopen ((const char*) filename, "rb");
+#endif
+ assert (file != nullptr);
+
+ i32_t fsz = fsize (file);
+
+ size_t bytes_read = fread (destbuf, 1, fsz, file);
+ assert (bytes_read > 0);
+
+ // null-terminate the buffer
+ destbuf[bytes_read] = 0;
+ // printf("File content: %s\n", destbuf);
+
+ fclose (file);
+
+ return 0;
+}
+
+i32_t
+read_u8str_sz (const char8_t *const filename, char8_t *const destbuf,
+ size_t *nread)
+{
+ FILE *file;
+
+#if is_win
+ fopen_s (&file, (const char*) filename, "rb,ccs=UTF-8");
+#else
+ file = fopen ((const char*) filename, "rb");
+#endif
+ assTcb (file != nullptr,
+ {
+ fprintf (stderr, "filename:%s\n", filename);
+ });
+
+ i32_t fsz = fsize (file);
+
+ size_t bytes_read = fread (destbuf, 1, fsz, file);
+ assert (bytes_read > 0);
+
+ // null-terminate the buffer
+ destbuf[bytes_read] = 0;
+ *nread = bytes_read;
+ // printf("File content: %s\n", destbuf);
+
+ fclose (file);
+
+ return 0;
+}
+
+void
+zero_trailing_white_char (char8_t *buf, const size_t len)
+{
+ for (size_t i = len - 1; i != 0; i--) // TODO ckd_add
+ {
+ if (buf[i] == '\n' || buf[i] == '\r' || buf[i] == '\t' || buf[i] == ' ')
+ buf[i] = 0;
+ else
+ break;
+ }
+}
+
+void
+zero_trailing_white_char2 (char8_t *buf, size_t *len)
+{
+ for (size_t i = *len - 1; i != 0; i--) // TODO ckd_add
+ {
+ if (buf[i] == '\n' || buf[i] == '\r' || buf[i] == '\t' || buf[i] == ' ')
+ {
+ buf[i] = 0;
+ *len -= 1;
+ }
+ else
+ break;
+ }
+}
+
+#ifdef is_unix
+FILE *
+unix_fd2strm_r (int fd)
+{
+ return fdopen (fd, "r");
+}
+#endif
+
+#ifdef is_win
+FILE *
+win_handle2strm (HANDLE h)
+{
+ int fd = _open_osfhandle ((intptr_t) h, _O_RDONLY);
+ assT (fd > 0);
+
+ FILE *file = _fdopen (fd, "r+");
+ assT (file != nullptr);
+ return file;
+}
+#endif
+
+#ifdef is_win
+int
+win_handle2fd (HANDLE h)
+{
+ int fd = _open_osfhandle ((intptr_t) h, _O_RDONLY);
+ assT (fd > 0);
+ return fd;
+}
+#endif
diff --git a/third_party/mcpc/tst/commtst.h b/third_party/mcpc/tst/commtst.h
new file mode 100644
index 0000000..49dcc93
--- /dev/null
+++ b/third_party/mcpc/tst/commtst.h
@@ -0,0 +1,147 @@
+#ifndef h_commtst
+#define h_commtst
+
+
+// clang-format off
+#ifdef is_unix
+#define _POSIX_SOURCE
+#include <ctype.h>
+#include <stdio.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#include <signal.h>
+#include <spawn.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#endif
+
+#ifdef is_win
+
+/* # define _WINSOCKAPI_ */
+#include <fcntl.h>
+#include <stdio.h>
+#include <winsock2.h>
+/* # include <windows.h> */
+#include <io.h>
+#include <ws2tcpip.h>
+#include "commtst.h"
+
+#endif
+// clang-format on
+
+#include <mcpc/_c23_keywords.h>
+#include <mcpc/_c23_uchar.h>
+#include <ext_string.h>
+#include <ext_stdint.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define ass0(f) \
+ do { \
+ int ec = (f); \
+ if (0 != ec) { \
+ fprintf(stderr, \
+ "%s:%d,ec:%d syserr:%s\n", \
+ __FILE__, \
+ __LINE__, \
+ ec, \
+ strerror(errno)); \
+ abort(); \
+ } \
+ } while (0)
+
+#define assT(f) \
+ do { \
+ if (!(f)) { \
+ fprintf(stderr, \
+ "abort@%s:%d,syserr:%s\n", \
+ __FILE__, \
+ __LINE__, \
+ strerror(errno)); \
+ abort(); \
+ } \
+ } while (0)
+
+#define assF(f) \
+ do { \
+ if ((f)) { \
+ fprintf(stderr, \
+ "abort@%s:%d,syserr:%s\n", \
+ __FILE__, \
+ __LINE__, \
+ strerror(errno)); \
+ abort(); \
+ } \
+ } while (0)
+
+#define assTcb(f, cb) \
+ do { \
+ if (!(f)) { \
+ { cb }; \
+ fprintf(stderr, "abort@%s:%d\n", __FILE__, __LINE__); \
+ abort(); \
+ } \
+ } while (0)
+
+#define BUFCAP 1024
+#define PORT 8080
+
+typedef int8_t i8_t;
+typedef uint8_t u8_t;
+typedef int16_t i16_t;
+typedef uint16_t u16_t;
+typedef int32_t i32_t;
+typedef uint32_t u32_t;
+typedef int64_t i64_t;
+typedef uint64_t u64_t;
+
+typedef char8_t ch8_t;
+
+typedef size_t sz_t;
+
+#ifdef is_win
+typedef SSIZE_T ssize_t;
+#endif
+
+void read_atmost (void *src, char *rbuf, const size_t nexact, size_t *nread);
+
+void recv_atmost (
+#ifdef is_unix
+ int csock,
+#elif defined(is_win)
+ SOCKET cosck,
+#endif
+ char *rbuf, const size_t nmost, size_t *nread);
+
+int32_t read_u8str (const char8_t * const filename, char8_t * const destbuf);
+
+int32_t read_u8str_sz (const char8_t * const filename, char8_t * const destbuf, size_t *nread);
+
+void zero_trailing_white_char (char8_t * buf, const size_t len);
+
+void zero_trailing_white_char2 (char8_t * buf, size_t *len);
+
+
+#ifdef is_unix
+FILE *unix_fd2strm_r (int fd);
+#endif
+
+#ifdef is_win
+FILE *win_handle2strm (HANDLE h);
+#endif
+
+#ifdef is_win
+int win_handle2fd (HANDLE h);
+#endif
+
+
+#endif
diff --git a/third_party/mcpc/tst/itst-svcalc-req-promptslist-1.json b/third_party/mcpc/tst/itst-svcalc-req-promptslist-1.json
new file mode 100644
index 0000000..2431074
--- /dev/null
+++ b/third_party/mcpc/tst/itst-svcalc-req-promptslist-1.json
@@ -0,0 +1 @@
+{"jsonrpc": "2.0", "method": "initialize", "params": {"clientInfo": {"name": "Llama Agent", "version": "0.1"}, "protocolVersion": "2024-11-05", "capabilities": { "tools": { "subscribe": true, "listChanged": true }, "resource": { "subscribe": true, "listChanged": true } }}, "id": 1}{"jsonrpc": "2.0", "method": "notifications/initialized", "params": {}, "id": 8899}{"id":123,"method":"prompts/list","params":[{"obfsarg":"{\"'}}"}]}
diff --git a/third_party/mcpc/tst/itst-svcalc-req-resourceslist-1.json b/third_party/mcpc/tst/itst-svcalc-req-resourceslist-1.json
new file mode 100644
index 0000000..cf23347
--- /dev/null
+++ b/third_party/mcpc/tst/itst-svcalc-req-resourceslist-1.json
@@ -0,0 +1 @@
+{"jsonrpc": "2.0", "method": "initialize", "params": {"clientInfo": {"name": "Llama Agent", "version": "0.1"}, "protocolVersion": "2024-11-05", "capabilities": { "tools": { "subscribe": true, "listChanged": true }, "resource": { "subscribe": true, "listChanged": true } }}, "id": 1}{"jsonrpc": "2.0", "method": "notifications/initialized", "params": {}, "id": 8899}{"id":123,"method":"resources/list","params":[{"obfsarg":"{\"'}}"}]}
diff --git a/third_party/mcpc/tst/itst-svcalc-req-toolslist-1.json b/third_party/mcpc/tst/itst-svcalc-req-toolslist-1.json
new file mode 100644
index 0000000..97be6cf
--- /dev/null
+++ b/third_party/mcpc/tst/itst-svcalc-req-toolslist-1.json
@@ -0,0 +1 @@
+{"jsonrpc": "2.0", "method": "initialize", "params": {"clientInfo": {"name": "Llama Agent", "version": "0.1"}, "protocolVersion": "2024-11-05", "capabilities": { "tools": { "subscribe": true, "listChanged": true }, "resource": { "subscribe": true, "listChanged": true } }}, "id": 1}{"jsonrpc": "2.0", "method": "notifications/initialized", "params": {}, "id": 8899}{"id":123,"method":"tools/list","params":[{"obfsarg":"{\"'}}"}]}
diff --git a/third_party/mcpc/tst/itst-svcalc.c b/third_party/mcpc/tst/itst-svcalc.c
new file mode 100644
index 0000000..3932991
--- /dev/null
+++ b/third_party/mcpc/tst/itst-svcalc.c
@@ -0,0 +1,207 @@
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <mcpc/_c23_keywords.h>
+#include <mcpc/mcpc.h>
+
+#include <ext_string.h>
+
+typedef int8_t i8_t;
+typedef uint8_t u8_t;
+typedef int16_t i16_t;
+typedef uint16_t u16_t;
+typedef int32_t i32_t;
+typedef uint32_t u32_t;
+typedef int64_t i64_t;
+typedef uint64_t u64_t;
+
+static void
+cb_calc (const mcpc_tool_t *tool, mcpc_ucbr_t *ucbr)
+{
+ i32_t a = 0;
+ i32_t b = 0;
+ char8_t op[64];
+ i32_t res = 0;
+
+ assert (0 == mcpc_tool_get_tpropval_i32 (tool, u8c1 ("a"), &a));
+ assert (0 == mcpc_tool_get_tpropval_i32 (tool, u8c1 ("b"), &b));
+ assert (0 == mcpc_tool_get_tpropval_u8str (tool, u8c1 ("op"), op, sizeof (op), nullptr));
+
+ if (false)
+ ;
+ else if (u8streq (op, u8c1 ("+")))
+ {
+ res = a + b;
+ }
+ else if (u8streq (op, u8c1 ("+")))
+ {
+ res = a + b;
+ }
+ else if (u8streq (op, u8c1 ("-")))
+ {
+ res = a - b;
+ }
+ else if (u8streq (op, u8c1 ("*")))
+ {
+ res = a * b;
+ }
+ else if (u8streq (op, u8c1 ("times")))
+ {
+ res = a * b;
+ }
+ else if (u8streq (op, u8c1 ("/")))
+ {
+ res = a / b;
+ }
+ else if (u8streq (op, u8c1 ("div")))
+ {
+ res = a / b;
+ }
+ else
+ {
+ mcpc_ucbr_toolcall_add_errmsg_printf8 (ucbr, u8c1 ("not matching \"any\" operator:%s"), op);
+ return;
+ }
+
+ mcpc_ucbr_toolcall_add_text_printf8 (ucbr, u8c1 ("%d"), res);
+}
+
+mcpc_tool_t *
+new_tool_calc ()
+{
+ const char8_t *name = u8c1 ("calc");
+ const char8_t *desc = u8c1 ("simple calculator");
+ mcpc_tool_t *tool = mcpc_tool_new2 (name, desc);
+
+ {
+ const char8_t *name = u8c1 ("a");
+ const mcpc_anytype_t type = MCPC_I32;
+ const char8_t *desc = u8c1 ("the first number(\"a\") to calculate");
+ mcpc_toolprop_t *prop = mcpc_toolprop_new2 (name, desc, type);
+ mcpc_tool_add_toolprop (tool, prop);
+ }
+ {
+ const char8_t *name = u8c1 ("b");
+ const mcpc_anytype_t type = MCPC_I32;
+ const char8_t *desc = u8c1 ("the second number to calculate");
+ mcpc_toolprop_t *prop = mcpc_toolprop_new2 (name, desc, type);
+ mcpc_tool_add_toolprop (tool, prop);
+ }
+ {
+ const char8_t *name = u8c1 ("op");
+ const mcpc_anytype_t type = MCPC_U8STR;
+ const char8_t *desc = //
+ u8c1 ("the calculating operator, available values " "are: \"+\",'-',\"*\",'/'");
+ mcpc_toolprop_t *prop = mcpc_toolprop_new2 (name, desc, type);
+ mcpc_tool_add_toolprop (tool, prop);
+ }
+
+ mcpc_tool_set_call_cb (tool, &cb_calc);
+
+ return tool;
+}
+
+int
+main_iostrm (int argc, const char *argv[])
+{
+ u8_t in_src = 0; // 1stdin 2file
+ char in_fpath[100];
+
+ for (int i = 2; i < argc; i++)
+ {
+ const char8_t *curr_arg = (const char8_t *) argv[i];
+ const char8_t *nex_arg = nullptr;
+ if (i < argc - 1)
+ nex_arg = (const char8_t *) argv[i + 1];
+ if (nex_arg != nullptr)
+ {
+ if (u8streq (curr_arg, u8c1 ("-i")))
+ {
+ if (u8streq (nex_arg, u8c1 ("-")))
+ {
+ in_src = 1;
+ }
+ else if (beg_with_alph ((const char8_t *) nex_arg))
+ {
+ // TODO nonascii filename?
+ /* memcpy (in_fpath, nex_arg, strlen (nex_arg)); */
+ strcpy (in_fpath, (const char *) nex_arg);
+ in_src = 2;
+ }
+ }
+ }
+ }
+
+ assert (in_src);
+
+ FILE *in_stream = nullptr;
+ switch (in_src)
+ {
+ case 1:
+ in_stream = stdin;
+ break;
+ case 2:
+ in_stream = fopen (in_fpath, "r");
+ if (!in_stream)
+ {
+ fprintf (stdout, "file:%s, err:%s", in_fpath, strerror (errno));
+ }
+ break;
+ }
+ assert (in_stream);
+
+ mcpc_server_t *server = mcpc_server_new_iostrm (in_stream, stdout);
+ mcpc_server_capa_enable_prmt (server);
+ mcpc_server_capa_enable_tool (server);
+ mcpc_server_capa_enable_rsc (server);
+ mcpc_server_capa_enable_complt (server);
+
+ mcpc_tool_t *tool = new_tool_calc ();
+ assert (0 == mcpc_server_add_tool (server, tool));
+
+ mcpc_errcode_t ec = mcpc_server_start (server);
+
+ mcpc_server_close (server);
+ fclose (in_stream);
+
+ return ec;
+}
+
+int
+main_tcp (int argc, const char *argv[])
+{
+ mcpc_server_t *server = mcpc_server_new_tcp ();
+ mcpc_server_capa_enable_prmt (server);
+ mcpc_server_capa_enable_tool (server);
+ mcpc_server_capa_enable_rsc (server);
+ mcpc_server_capa_enable_complt (server);
+
+ mcpc_tool_t *tool = new_tool_calc ();
+ assert (0 == mcpc_server_add_tool (server, tool));
+
+ mcpc_errcode_t ec = mcpc_server_start (server);
+
+ mcpc_server_close (server);
+ return ec;
+}
+
+int
+main (int argc, const char *argv[])
+{
+ assert (argc >= 2);
+ bool as_iostrm = strcmp (argv[1], "iostrm") == 0;
+ bool as_tcp = strcmp (argv[1], "tcp") == 0;
+ assert (as_iostrm || as_tcp);
+ // bool as_iostrm = false;
+ // bool as_tcp = true;
+ if (as_tcp)
+ return main_tcp (argc, argv);
+ if (as_iostrm)
+ return main_iostrm (argc, argv);
+}