summaryrefslogtreecommitdiff
path: root/src/mcp/tools/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/mcp/tools/common')
-rw-r--r--src/mcp/tools/common/common.c117
-rw-r--r--src/mcp/tools/common/common.h19
-rw-r--r--src/mcp/tools/common/context.c88
-rw-r--r--src/mcp/tools/common/context.h20
-rw-r--r--src/mcp/tools/common/registry.c11
-rw-r--r--src/mcp/tools/common/registry.h13
-rw-r--r--src/mcp/tools/common/tools.h10
7 files changed, 278 insertions, 0 deletions
diff --git a/src/mcp/tools/common/common.c b/src/mcp/tools/common/common.c
new file mode 100644
index 0000000..71b6f68
--- /dev/null
+++ b/src/mcp/tools/common/common.c
@@ -0,0 +1,117 @@
+#include "mcp/tools/common/common.h"
+
+#include <stdlib.h>
+
+#include "base/allocator.h"
+
+void
+tiny_mcp_tool_add_error(mcpc_ucbr_t *result, const char *code, const char *message)
+{
+ tiny_json_buffer_t json;
+ tiny_json_init(&json);
+ tiny_json_raw(&json, "{\"status\":\"ERROR\",\"error\":{\"code\":");
+ tiny_json_string(&json, code);
+ tiny_json_raw(&json, ",\"message\":");
+ tiny_json_string(&json, message);
+ tiny_json_raw(&json, "}}");
+ mcpc_toolcall_result_add_errmsg_printf8(
+ result, (const char8_t *)"%s", json.data ? json.data : "{}");
+ tiny_json_free(&json);
+}
+
+void
+tiny_mcp_tool_add_json(mcpc_ucbr_t *result, tiny_json_buffer_t *json)
+{
+ if (json->failed)
+ {
+ tiny_mcp_tool_add_error(result, "INTERNAL_ERROR", "response allocation failed");
+ return;
+ }
+ mcpc_toolcall_result_add_text_printf8(
+ result, (const char8_t *)"%s", json->data ? json->data : "{}");
+}
+
+char *
+tiny_mcp_tool_get_string(const mcpc_tool_t *tool, const char *name, size_t maximum)
+{
+ char *value = malloc(maximum + 1);
+ size_t length = 0;
+ if (!value
+ || mcpc_tool_get_tpropval_u8str(
+ tool, (const char8_t *)name, (char8_t *)value, maximum + 1, &length)
+ != MCPC_EC_0
+ || length > maximum)
+ {
+ free(value);
+ return NULL;
+ }
+ value[length] = '\0';
+ if (!tiny_utf8_valid(value, length))
+ {
+ free(value);
+ return NULL;
+ }
+ return value;
+}
+
+void
+tiny_mcp_tool_append_message(tiny_json_buffer_t *json, const tiny_message_t *message)
+{
+ size_t index;
+ tiny_json_raw(json, "{\"message_id\":");
+ tiny_json_string(json, message->message_id);
+ tiny_json_raw(json, ",\"session_id\":");
+ tiny_json_string(json, message->session_id);
+ tiny_json_raw(json, ",\"timestamp\":");
+ tiny_json_i64(json, message->timestamp_ms);
+ tiny_json_raw(json, ",\"role\":");
+ tiny_json_string(json, tiny_role_name(message->role));
+ tiny_json_raw(json, ",\"content\":");
+ tiny_json_string(json, message->content);
+ tiny_json_raw(json, ",\"metadata\":{");
+ if (message->metadata.tool_call_id)
+ {
+ tiny_json_raw(json, "\"tool_call_id\":");
+ tiny_json_string(json, message->metadata.tool_call_id);
+ tiny_json_raw(json, ",");
+ }
+ tiny_json_raw(json, "\"tags\":[");
+ for (index = 0; index < message->metadata.tag_count; ++index)
+ {
+ if (index)
+ {
+ tiny_json_raw(json, ",");
+ }
+ tiny_json_string(json, message->metadata.tags[index]);
+ }
+ tiny_json_raw(json, "],\"memory_tier\":");
+ tiny_json_string(json, tiny_tier_name(message->metadata.memory_tier));
+ tiny_json_raw(json, "}}");
+}
+
+mcpc_tool_t *
+tiny_mcp_tool_new(const char *name, const char *description, mcpc_tcallcb_t callback)
+{
+ mcpc_tool_t *tool = mcpc_tool_new2((const char8_t *)name, (const char8_t *)description);
+ if (!tool || mcpc_tool_set_call_cb(tool, callback) != MCPC_EC_0)
+ {
+ return NULL;
+ }
+ return tool;
+}
+
+bool
+tiny_mcp_tool_add_property(mcpc_tool_t *tool,
+ const char *name,
+ const char *description,
+ mcpc_anytype_t type)
+{
+ mcpc_toolprop_t *property
+ = mcpc_toolprop_new2((const char8_t *)name, (const char8_t *)description, type);
+ if (!property)
+ {
+ return false;
+ }
+ mcpc_tool_add_toolprop(tool, property);
+ return true;
+}
diff --git a/src/mcp/tools/common/common.h b/src/mcp/tools/common/common.h
new file mode 100644
index 0000000..6b1cc96
--- /dev/null
+++ b/src/mcp/tools/common/common.h
@@ -0,0 +1,19 @@
+#ifndef TINY_CONVMEM_MCP_TOOLS_COMMON_H
+#define TINY_CONVMEM_MCP_TOOLS_COMMON_H
+
+#include "domain/message.h"
+#include "mcp/json.h"
+#include <mcpc/mcpc.h>
+
+void tiny_mcp_tool_add_error(mcpc_ucbr_t *result, const char *code, const char *message);
+void tiny_mcp_tool_add_json(mcpc_ucbr_t *result, tiny_json_buffer_t *json);
+char *tiny_mcp_tool_get_string(const mcpc_tool_t *tool, const char *name, size_t maximum);
+void tiny_mcp_tool_append_message(tiny_json_buffer_t *json, const tiny_message_t *message);
+
+mcpc_tool_t *tiny_mcp_tool_new(const char *name, const char *description, mcpc_tcallcb_t callback);
+bool tiny_mcp_tool_add_property(mcpc_tool_t *tool,
+ const char *name,
+ const char *description,
+ mcpc_anytype_t type);
+
+#endif
diff --git a/src/mcp/tools/common/context.c b/src/mcp/tools/common/context.c
new file mode 100644
index 0000000..1a469e0
--- /dev/null
+++ b/src/mcp/tools/common/context.c
@@ -0,0 +1,88 @@
+#include "mcp/tools/common/context.h"
+
+#include <stdlib.h>
+
+#include "base/allocator.h"
+
+/* mcpc's callback API has no user-data parameter. The stdio server is single-instance. */
+static tiny_mcp_tools_context_t *active_context;
+
+tiny_search_index_t *
+tiny_mcp_tools_build_search_index(const tiny_store_t *store)
+{
+ tiny_search_index_t *index = tiny_search_index_create();
+ size_t position;
+ if (!index)
+ {
+ return NULL;
+ }
+ for (position = 0; position < tiny_store_entry_count(store); ++position)
+ {
+ if (!tiny_search_index_add(index, &tiny_store_entry(store, position)->message, position))
+ {
+ tiny_search_index_free(index);
+ return NULL;
+ }
+ }
+ return index;
+}
+
+tiny_mcp_tools_context_t *
+tiny_mcp_tools_context_new(tiny_store_t *store)
+{
+ tiny_mcp_tools_context_t *context;
+ if (!store)
+ {
+ return NULL;
+ }
+ context = malloc(sizeof(*context));
+ if (!context)
+ {
+ return NULL;
+ }
+ context->store = store;
+ context->search_index = tiny_mcp_tools_build_search_index(store);
+ if (!context->search_index)
+ {
+ free(context);
+ return NULL;
+ }
+ return context;
+}
+
+void
+tiny_mcp_tools_context_free(tiny_mcp_tools_context_t *context)
+{
+ if (!context)
+ {
+ return;
+ }
+ tiny_search_index_free(context->search_index);
+ free(context);
+}
+
+bool
+tiny_mcp_tools_context_activate(tiny_mcp_tools_context_t *context)
+{
+ if (!context || active_context)
+ {
+ return false;
+ }
+ active_context = context;
+ return true;
+}
+
+void
+tiny_mcp_tools_context_deactivate(tiny_mcp_tools_context_t *context)
+{
+ if (active_context == context)
+ {
+ active_context = NULL;
+ }
+}
+
+tiny_mcp_tools_context_t *
+tiny_mcp_tools_context_current(void)
+{
+ return active_context;
+}
diff --git a/src/mcp/tools/common/context.h b/src/mcp/tools/common/context.h
new file mode 100644
index 0000000..0847d4b
--- /dev/null
+++ b/src/mcp/tools/common/context.h
@@ -0,0 +1,20 @@
+#ifndef TINY_CONVMEM_MCP_TOOLS_CONTEXT_H
+#define TINY_CONVMEM_MCP_TOOLS_CONTEXT_H
+
+#include "domain/search.h"
+#include "storage/store.h"
+
+typedef struct tiny_mcp_tools_context
+{
+ tiny_store_t *store;
+ tiny_search_index_t *search_index;
+} tiny_mcp_tools_context_t;
+
+tiny_mcp_tools_context_t *tiny_mcp_tools_context_new(tiny_store_t *store);
+void tiny_mcp_tools_context_free(tiny_mcp_tools_context_t *context);
+bool tiny_mcp_tools_context_activate(tiny_mcp_tools_context_t *context);
+void tiny_mcp_tools_context_deactivate(tiny_mcp_tools_context_t *context);
+tiny_mcp_tools_context_t *tiny_mcp_tools_context_current(void);
+tiny_search_index_t *tiny_mcp_tools_build_search_index(const tiny_store_t *store);
+
+#endif
diff --git a/src/mcp/tools/common/registry.c b/src/mcp/tools/common/registry.c
new file mode 100644
index 0000000..9aa1dff
--- /dev/null
+++ b/src/mcp/tools/common/registry.c
@@ -0,0 +1,11 @@
+#include "mcp/tools/common/tools.h"
+
+#include "mcp/tools/common/registry.h"
+
+bool
+tiny_mcp_tools_register(mcpc_server_t *server)
+{
+ return tiny_mcp_register_write_message(server) && tiny_mcp_register_read_messages(server)
+ && tiny_mcp_register_search_messages(server) && tiny_mcp_register_delete_message(server)
+ && tiny_mcp_register_rebuild_index(server) && tiny_mcp_register_get_system_health(server);
+}
diff --git a/src/mcp/tools/common/registry.h b/src/mcp/tools/common/registry.h
new file mode 100644
index 0000000..bbc68d0
--- /dev/null
+++ b/src/mcp/tools/common/registry.h
@@ -0,0 +1,13 @@
+#ifndef TINY_CONVMEM_MCP_TOOLS_REGISTRY_H
+#define TINY_CONVMEM_MCP_TOOLS_REGISTRY_H
+
+#include <mcpc/mcpc.h>
+
+bool tiny_mcp_register_write_message(mcpc_server_t *server);
+bool tiny_mcp_register_read_messages(mcpc_server_t *server);
+bool tiny_mcp_register_search_messages(mcpc_server_t *server);
+bool tiny_mcp_register_delete_message(mcpc_server_t *server);
+bool tiny_mcp_register_rebuild_index(mcpc_server_t *server);
+bool tiny_mcp_register_get_system_health(mcpc_server_t *server);
+
+#endif
diff --git a/src/mcp/tools/common/tools.h b/src/mcp/tools/common/tools.h
new file mode 100644
index 0000000..b06321c
--- /dev/null
+++ b/src/mcp/tools/common/tools.h
@@ -0,0 +1,10 @@
+#ifndef TINY_CONVMEM_MCP_TOOLS_H
+#define TINY_CONVMEM_MCP_TOOLS_H
+
+#include "mcp/tools/common/context.h"
+#define MCPC_C23PTCH_UCHAR2 1
+#include <mcpc/mcpc.h>
+
+bool tiny_mcp_tools_register(mcpc_server_t *server);
+
+#endif