summaryrefslogtreecommitdiff
path: root/third_party/mcpc/src/server.c
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/src/server.c
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/src/server.c')
-rw-r--r--third_party/mcpc/src/server.c1830
1 files changed, 1830 insertions, 0 deletions
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;
+ }
+}