summaryrefslogtreecommitdiff
path: root/src/mcp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mcp')
-rw-r--r--src/mcp/tools/common/registry.c3
-rw-r--r--src/mcp/tools/common/registry.h1
-rw-r--r--src/mcp/tools/import_messages.c562
-rw-r--r--src/mcp/tools/read_messages.c100
4 files changed, 2 insertions, 664 deletions
diff --git a/src/mcp/tools/common/registry.c b/src/mcp/tools/common/registry.c
index cc3fa22..1bdafc3 100644
--- a/src/mcp/tools/common/registry.c
+++ b/src/mcp/tools/common/registry.c
@@ -8,6 +8,5 @@ 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_update_message(server)
&& tiny_mcp_register_delete_message(server) && tiny_mcp_register_rebuild_index(server)
- && tiny_mcp_register_get_system_health(server)
- && tiny_mcp_register_import_messages(server);
+ && tiny_mcp_register_get_system_health(server);
}
diff --git a/src/mcp/tools/common/registry.h b/src/mcp/tools/common/registry.h
index c873869..0d67eec 100644
--- a/src/mcp/tools/common/registry.h
+++ b/src/mcp/tools/common/registry.h
@@ -11,6 +11,5 @@ bool tiny_mcp_register_update_message(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);
-bool tiny_mcp_register_import_messages(mcpc_server_t *server);
#endif
diff --git a/src/mcp/tools/import_messages.c b/src/mcp/tools/import_messages.c
deleted file mode 100644
index c46b016..0000000
--- a/src/mcp/tools/import_messages.c
+++ /dev/null
@@ -1,562 +0,0 @@
-#include "mcp/tools/common/registry.h"
-
-#include "mcp/tools/common/common.h"
-#include "mcp/tools/common/context.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "mjson.h"
-#include "storage/store.h"
-
-#define IMPORT_MAX_LINE (TINY_CONTENT_MAX + 4096U)
-
-typedef struct import_record_list
-{
- tiny_import_record_t *items;
- size_t count;
- size_t capacity;
-} import_record_list_t;
-
-static void
-import_record_list_free(import_record_list_t *list)
-{
- size_t index;
- if (!list)
- {
- return;
- }
- for (index = 0; index < list->count; ++index)
- {
- tiny_message_free(&list->items[index].message);
- }
- free(list->items);
- list->items = NULL;
- list->count = 0;
- list->capacity = 0;
-}
-
-static bool
-import_record_list_push(import_record_list_t *list)
-{
- tiny_import_record_t *resized;
- size_t capacity;
- if (list->count < list->capacity)
- {
- return true;
- }
- capacity = list->capacity == 0 ? 32 : list->capacity * 2;
- resized = realloc(list->items, capacity * sizeof(*resized));
- if (!resized)
- {
- return false;
- }
- list->items = resized;
- list->capacity = capacity;
- return true;
-}
-
-static bool
-import_get_string(const char *json, int len, const char *path, char *out, size_t out_size)
-{
- int decoded = mjson_get_string(json, len, path, out, (int)out_size);
- return decoded > 0 && (size_t)decoded < out_size;
-}
-
-static bool
-import_get_i64(const char *json, int len, const char *path, int64_t *value)
-{
- double number;
- if (mjson_get_number(json, len, path, &number) != 1)
- {
- return false;
- }
- *value = (int64_t)number;
- return true;
-}
-
-static bool
-import_get_u64(const char *json, int len, const char *path, uint64_t *value)
-{
- double number;
- if (mjson_get_number(json, len, path, &number) != 1 || number < 0)
- {
- return false;
- }
- *value = (uint64_t)number;
- return true;
-}
-
-static bool
-import_parse_string_array(const char *json,
- int len,
- const char *path,
- char ***output,
- size_t *count,
- size_t max_count,
- size_t max_length)
-{
- int offset = 0, key_offset, key_length, value_offset, value_length, value_type;
- char *array_json = NULL;
- int array_len = 0;
- size_t index = 0;
- *output = NULL;
- *count = 0;
- if (mjson_find(json, len, path, &array_json, &array_len) == 0)
- {
- return true;
- }
- if (array_json == NULL || mjson_find(array_json, array_len, "$", NULL, NULL) != MJSON_TOK_ARRAY)
- {
- return false;
- }
- *output = calloc(max_count, sizeof(char *));
- if (!*output)
- {
- return false;
- }
- while ((offset = mjson_next(array_json,
- array_len,
- offset,
- &key_offset,
- &key_length,
- &value_offset,
- &value_length,
- &value_type))
- != 0)
- {
- char value[512];
- int decoded;
- if (value_type != MJSON_TOK_STRING || index >= max_count
- || (decoded = mjson_get_string(
- array_json + value_offset, value_length, "$", value, sizeof(value)))
- <= 0
- || (size_t)decoded > max_length || decoded >= (int)sizeof(value))
- {
- break;
- }
- (*output)[index] = malloc((size_t)decoded + 1);
- if (!(*output)[index])
- {
- break;
- }
- memcpy((*output)[index], value, (size_t)decoded);
- (*output)[index][decoded] = '\0';
- index++;
- }
- *count = index;
- if (index > 0 && offset != 0)
- {
- /* mjson_next returned non-zero because of a parse/validation failure. */
- return false;
- }
- return true;
-}
-
-static bool
-import_parse_relations(const char *json, int len, const char *path, tiny_metadata_t *metadata)
-{
- int offset = 0, key_offset, key_length, value_offset, value_length, value_type;
- char *array_json = NULL;
- int array_len = 0;
- size_t index = 0;
- if (mjson_find(json, len, path, &array_json, &array_len) == 0)
- {
- return true;
- }
- if (array_json == NULL || mjson_find(array_json, array_len, "$", NULL, NULL) != MJSON_TOK_ARRAY)
- {
- return false;
- }
- metadata->relations = calloc(TINY_RELATION_COUNT_MAX, sizeof(tiny_relation_t));
- if (!metadata->relations)
- {
- return false;
- }
- while ((offset = mjson_next(array_json,
- array_len,
- offset,
- &key_offset,
- &key_length,
- &value_offset,
- &value_length,
- &value_type))
- != 0)
- {
- char type_name[32];
- char message_id[32];
- int decoded;
- tiny_relation_t *relation;
- if (value_type != MJSON_TOK_OBJECT || index >= TINY_RELATION_COUNT_MAX
- || mjson_get_string(
- array_json + value_offset, value_length, "$.type", type_name, sizeof(type_name))
- <= 0
- || (decoded = mjson_get_string(array_json + value_offset,
- value_length,
- "$.message_id",
- message_id,
- sizeof(message_id)))
- <= 0)
- {
- return false;
- }
- relation = &metadata->relations[index];
- if (!tiny_relation_type_parse(type_name, &relation->type)
- || !tiny_message_id_valid(message_id))
- {
- return false;
- }
- memcpy(relation->message_id, message_id, (size_t)decoded);
- relation->message_id[decoded] = '\0';
- index++;
- }
- metadata->relation_count = index;
- return true;
-}
-
-static bool
-import_parse_metadata(const char *json, int len, tiny_message_t *message)
-{
- char tier_name[32] = { 0 };
- char kind_name[32] = { 0 };
- char tool_call_id[TINY_TOOL_CALL_ID_MAX + 1] = { 0 };
- char summary[TINY_SUMMARY_MAX + 1] = { 0 };
- double importance_number = 0;
- bool has_tool_call_id, has_summary, has_importance;
- has_tool_call_id = mjson_find(json, len, "$.metadata.tool_call_id", NULL, NULL) != 0
- && import_get_string(
- json, len, "$.metadata.tool_call_id", tool_call_id, sizeof(tool_call_id));
- has_summary = mjson_find(json, len, "$.metadata.summary", NULL, NULL) != 0
- && import_get_string(json, len, "$.metadata.summary", summary, sizeof(summary));
- has_importance = mjson_get_number(json, len, "$.metadata.importance", &importance_number) == 1;
- if (!import_get_string(json, len, "$.metadata.memory_tier", tier_name, sizeof(tier_name))
- || !import_get_string(json, len, "$.metadata.memory_kind", kind_name, sizeof(kind_name))
- || !import_parse_string_array(json,
- len,
- "$.metadata.tags",
- &message->metadata.tags,
- &message->metadata.tag_count,
- TINY_TAG_COUNT_MAX,
- TINY_TAG_MAX)
- || !import_parse_relations(json, len, "$.metadata.relations", &message->metadata)
- || !import_parse_string_array(json,
- len,
- "$.metadata.source_message_ids",
- &message->metadata.source_message_ids,
- &message->metadata.source_count,
- TINY_SOURCE_COUNT_MAX,
- TINY_MESSAGE_ID_LENGTH))
- {
- return false;
- }
- if (!tiny_tier_parse(tier_name, &message->metadata.memory_tier)
- || !tiny_kind_parse(kind_name, &message->metadata.memory_kind))
- {
- return false;
- }
- if (has_tool_call_id)
- {
- message->metadata.tool_call_id = malloc(strlen(tool_call_id) + 1);
- if (!message->metadata.tool_call_id)
- {
- return false;
- }
- memcpy(message->metadata.tool_call_id, tool_call_id, strlen(tool_call_id) + 1);
- }
- if (has_summary)
- {
- message->metadata.summary = malloc(strlen(summary) + 1);
- if (!message->metadata.summary)
- {
- return false;
- }
- memcpy(message->metadata.summary, summary, strlen(summary) + 1);
- }
- if (has_importance)
- {
- if (importance_number < 0 || importance_number > 100)
- {
- return false;
- }
- message->metadata.importance = (uint8_t)importance_number;
- }
- return true;
-}
-
-static bool
-import_manifest_valid(const char *line, int len)
-{
- char type_name[64];
- double version = 0;
- if (!import_get_string(line, len, "$.type", type_name, sizeof(type_name))
- || strcmp(type_name, "tiny-convmem-manifest") != 0
- || mjson_get_number(line, len, "$.format_version", &version) != 1)
- {
- return false;
- }
- return (int)version == 2;
-}
-
-static bool
-import_parse_del_record(const char *line, int len, tiny_import_record_t *record)
-{
- char type_name[32];
- tiny_message_init(&record->message);
- if (!import_get_string(line, len, "$.type", type_name, sizeof(type_name))
- || strcmp(type_name, "DEL") != 0
- || !import_get_string(
- line, len, "$.message_id", record->message.message_id, sizeof(record->message.message_id))
- || !tiny_message_id_valid(record->message.message_id))
- {
- return false;
- }
- record->deleted = true;
- return true;
-}
-
-static bool
-import_parse_message_record(const char *line, int len, tiny_import_record_t *record)
-{
- char role_name[32];
- char session_id[TINY_SESSION_ID_MAX + 1];
- char content_buffer[4096];
- tiny_message_init(&record->message);
- record->deleted = false;
- if (!import_get_string(
- line, len, "$.message_id", record->message.message_id, sizeof(record->message.message_id))
- || !import_get_string(line, len, "$.session_id", session_id, sizeof(session_id))
- || !import_get_string(line, len, "$.role", role_name, sizeof(role_name))
- || !import_get_i64(line, len, "$.timestamp", &record->message.timestamp_ms)
- || !import_get_i64(line, len, "$.updated_at", &record->message.updated_at_ms)
- || !import_get_u64(line, len, "$.revision", &record->message.revision))
- {
- return false;
- }
- if (!tiny_message_id_valid(record->message.message_id)
- || !tiny_role_parse(role_name, &record->message.role) || record->message.timestamp_ms < 0
- || record->message.updated_at_ms < 0 || record->message.revision == 0
- || record->message.revision > INT32_MAX)
- {
- return false;
- }
- if (mjson_find(line, len, "$.content", NULL, NULL) == 0)
- {
- return false;
- }
- if (mjson_get_string(line, len, "$.content", content_buffer, sizeof(content_buffer)) > 0)
- {
- size_t length = strlen(content_buffer);
- if (length == 0 || length > TINY_CONTENT_MAX)
- {
- return false;
- }
- record->message.content = malloc(length + 1);
- if (!record->message.content)
- {
- return false;
- }
- memcpy(record->message.content, content_buffer, length + 1);
- }
- else
- {
- const char *content = NULL;
- int content_len = 0;
- if (mjson_find(line, len, "$.content", &content, &content_len) == 0 || !content
- || content_len < 2)
- {
- return false;
- }
- if ((size_t)(content_len - 2) == 0 || (size_t)(content_len - 2) > TINY_CONTENT_MAX)
- {
- return false;
- }
- record->message.content = malloc((size_t)content_len - 1);
- if (!record->message.content)
- {
- return false;
- }
- memcpy(record->message.content, content + 1, (size_t)content_len - 2);
- record->message.content[(size_t)content_len - 2] = '\0';
- }
- if (!tiny_utf8_valid(record->message.content, strlen(record->message.content)))
- {
- return false;
- }
- record->message.session_id = malloc(strlen(session_id) + 1);
- if (!record->message.session_id)
- {
- return false;
- }
- memcpy(record->message.session_id, session_id, strlen(session_id) + 1);
- if (!import_parse_metadata(line, len, &record->message)
- || !tiny_metadata_valid(&record->message.metadata))
- {
- tiny_message_free(&record->message);
- tiny_message_init(&record->message);
- return false;
- }
- return true;
-}
-
-static bool
-import_parse_jsonl(const char *jsonl, import_record_list_t *records)
-{
- const char *line_start = jsonl;
- const char *line_end;
- bool manifest_seen = false;
- while (*line_start)
- {
- int length;
- tiny_import_record_t record;
- line_end = strchr(line_start, '\n');
- if (line_end == NULL)
- {
- line_end = line_start + strlen(line_start);
- }
- length = (int)(line_end - line_start);
- if (length > 0)
- {
- if (!manifest_seen)
- {
- if (!import_manifest_valid(line_start, length))
- {
- return false;
- }
- manifest_seen = true;
- }
- else if (import_parse_del_record(line_start, length, &record))
- {
- if (!import_record_list_push(records))
- {
- return false;
- }
- records->items[records->count++] = record;
- }
- else if (import_parse_message_record(line_start, length, &record))
- {
- if (!import_record_list_push(records))
- {
- tiny_message_free(&record.message);
- return false;
- }
- records->items[records->count++] = record;
- }
- else
- {
- return false;
- }
- }
- if (*line_end == '\0')
- {
- break;
- }
- line_start = line_end + 1;
- }
- return manifest_seen;
-}
-
-static void
-call_import_messages(const mcpc_tool_t *tool, mcpc_ucbr_t *result)
-{
- tiny_mcp_tools_context_t *context = tiny_mcp_tools_context_current();
- char *jsonl = tiny_mcp_tool_get_string(tool, "jsonl", TINY_CONTENT_MAX);
- char *policy_name = tiny_mcp_tool_get_string(tool, "policy", 16);
- tiny_import_policy_t policy = TINY_IMPORT_OVERWRITE;
- import_record_list_t records = { NULL, 0, 0 };
- size_t imported = 0, rejected = 0;
- tiny_json_buffer_t json;
- tiny_store_status_t status;
-
- if (!context || !jsonl)
- {
- tiny_mcp_tool_add_error(result, "VALIDATION_ERROR", "invalid import_messages arguments");
- free(jsonl);
- free(policy_name);
- return;
- }
- if (policy_name && policy_name[0])
- {
- if (strcmp(policy_name, "overwrite") == 0)
- {
- policy = TINY_IMPORT_OVERWRITE;
- }
- else if (strcmp(policy_name, "reject") == 0)
- {
- policy = TINY_IMPORT_REJECT;
- }
- else
- {
- tiny_mcp_tool_add_error(result, "VALIDATION_ERROR", "invalid import policy");
- free(jsonl);
- free(policy_name);
- return;
- }
- }
- if (!import_parse_jsonl(jsonl, &records))
- {
- tiny_mcp_tool_add_error(result, "VALIDATION_ERROR", "invalid import JSONL");
- import_record_list_free(&records);
- free(jsonl);
- free(policy_name);
- return;
- }
- status = tiny_store_import(
- context->store, records.items, records.count, policy, &imported, &rejected);
- import_record_list_free(&records);
- tiny_json_init(&json);
- if (status == TINY_STORE_OK)
- {
- tiny_json_raw(&json, "{\"status\":\"OK\",\"imported\":");
- tiny_json_u64(&json, (uint64_t)imported);
- tiny_json_raw(&json, ",\"rejected\":");
- tiny_json_u64(&json, (uint64_t)rejected);
- tiny_json_raw(&json, "}");
- }
- else if (status == TINY_STORE_CONFLICT)
- {
- tiny_json_raw(&json,
- "{\"status\":\"ERROR\",\"error\":{\"code\":\"CONFLICT\","
- "\"message\":\"import conflict with existing records\"}}");
- }
- else if (status == TINY_STORE_CORRUPT)
- {
- tiny_json_raw(&json,
- "{\"status\":\"ERROR\",\"error\":{\"code\":\"CORRUPT_STORE\","
- "\"message\":\"import JSONL contained invalid records\"}}");
- }
- else
- {
- tiny_json_raw(&json,
- "{\"status\":\"ERROR\",\"error\":{\"code\":\"INTERNAL_ERROR\","
- "\"message\":\"import failed\"}}");
- }
- tiny_mcp_tool_add_json(result, &json);
- tiny_json_free(&json);
- free(jsonl);
- free(policy_name);
-}
-
-bool
-tiny_mcp_register_import_messages(mcpc_server_t *server)
-{
- mcpc_tool_t *tool = tiny_mcp_tool_new(
- "import_messages",
- "Atomically import a JSONL export from read_messages view=export. The manifest line is "
- "required; subsequent lines are PUT messages or DEL tombstones. Conflicting message IDs "
- "are resolved by the policy argument.",
- call_import_messages);
- return tool
- && tiny_mcp_tool_add_property(tool,
- "jsonl",
- "JSONL string containing a manifest line followed by PUT "
- "message lines and optional DEL tombstone lines.",
- MCPC_U8STR,
- true)
- && tiny_mcp_tool_add_property(tool,
- "policy",
- "Conflict policy: overwrite (default) replaces existing "
- "message IDs, reject refuses duplicate IDs.",
- MCPC_U8STR,
- false)
- && mcpc_server_add_tool(server, tool) == MCPC_EC_0;
-}
diff --git a/src/mcp/tools/read_messages.c b/src/mcp/tools/read_messages.c
index 647e6f3..109becb 100644
--- a/src/mcp/tools/read_messages.c
+++ b/src/mcp/tools/read_messages.c
@@ -729,86 +729,6 @@ call_sessions_view(tiny_mcp_tools_context_t *context, const mcpc_tool_t *tool, m
}
static void
-call_export_view(tiny_mcp_tools_context_t *context, const mcpc_tool_t *tool, mcpc_ucbr_t *result)
-{
- char *session = tiny_mcp_tool_get_string(tool, "session_id", TINY_SESSION_ID_MAX);
- char *cursor = tiny_mcp_tool_get_string(tool, "cursor", CURSOR_TEXT_MAX);
- int32_t limit = 500;
- char *include_deleted_text = tiny_mcp_tool_get_string(tool, "include_deleted", 8);
- bool include_deleted = include_deleted_text && strcmp(include_deleted_text, "true") == 0;
- bool valid;
- size_t index = simple_cursor(cursor, &valid), emitted = 0, start_index;
- tiny_json_buffer_t json, lines;
- size_t outer_size = 64;
- bool truncated = false;
- mcpc_errcode_t limit_status = mcpc_tool_get_tpropval_i32(tool, (const char8_t *)"limit", &limit);
- if (!context || !valid || limit < 1 || limit > 1000
- || (limit_status != MCPC_EC_0 && limit_status != MCPC_EC_ANYDATA_NOTFOUND))
- {
- tiny_mcp_tool_add_error(result, "VALIDATION_ERROR", "invalid export view arguments");
- free(session);
- free(cursor);
- return;
- }
- start_index = index;
- tiny_json_init(&lines);
- if (index == 0)
- {
- tiny_json_raw(&lines, "{\"type\":\"tiny-convmem-manifest\",\"format_version\":2}\n");
- }
- for (; index < tiny_store_entry_count(context->store) && emitted < (size_t)limit; ++index)
- {
- const tiny_store_entry_t *entry = tiny_store_entry(context->store, index);
- tiny_json_buffer_t item;
- size_t line_overhead;
- if (!entry || (entry->deleted && !include_deleted)
- || (session && session[0] && strcmp(session, entry->message.session_id) != 0))
- {
- continue;
- }
- tiny_json_init(&item);
- if (entry->deleted)
- {
- tiny_json_raw(&item, "{\"type\":\"DEL\",\"message_id\":");
- tiny_json_string(&item, entry->message.message_id);
- tiny_json_raw(&item, "}");
- }
- else
- {
- tiny_mcp_tool_append_message(&item, &entry->message);
- }
- line_overhead = item.length + 1;
- if (item.failed || outer_size + lines.length + line_overhead + 2 > READ_RESPONSE_BUDGET)
- {
- truncated = true;
- tiny_json_free(&item);
- break;
- }
- tiny_json_raw_n(&lines, item.data, item.length);
- tiny_json_raw(&lines, "\n");
- emitted++;
- tiny_json_free(&item);
- }
- tiny_json_init(&json);
- tiny_json_raw(&json, "{\"jsonl\":");
- tiny_json_string(&json, lines.data ? lines.data : "");
- if (truncated || index < tiny_store_entry_count(context->store))
- {
- char next[32];
- snprintf(next, sizeof(next), "%zu", index > start_index ? index : start_index + 1);
- tiny_json_raw(&json, ",\"nextCursor\":");
- tiny_json_string(&json, next);
- tiny_json_raw(&json, ",\"truncated\":true");
- }
- tiny_json_raw(&json, "}");
- tiny_mcp_tool_add_json(result, &json);
- tiny_json_free(&json);
- tiny_json_free(&lines);
- free(session);
- free(cursor);
-}
-
-static void
call_read_messages(const mcpc_tool_t *tool, mcpc_ucbr_t *result)
{
tiny_mcp_tools_context_t *context = tiny_mcp_tools_context_current();
@@ -841,14 +761,6 @@ call_read_messages(const mcpc_tool_t *tool, mcpc_ucbr_t *result)
free(cursor_text);
return;
}
- if (view && strcmp(view, "export") == 0)
- {
- call_export_view(context, tool, result);
- free(view);
- free(session);
- free(cursor_text);
- return;
- }
if (view && strcmp(view, "history") != 0)
{
tiny_mcp_tool_add_error(result, "VALIDATION_ERROR", "unsupported read_messages view");
@@ -983,11 +895,7 @@ tiny_mcp_register_read_messages(mcpc_server_t *server)
call_read_messages);
return tool
&& tiny_mcp_tool_add_property(
- tool,
- "view",
- "Read view: history (default), context, sessions, or export.",
- MCPC_U8STR,
- false)
+ tool, "view", "Read view: history (default), context, or sessions.", MCPC_U8STR, false)
&& tiny_mcp_tool_add_property(tool,
"session_id",
"Conversation session whose chronological history to read.",
@@ -1022,11 +930,5 @@ tiny_mcp_register_read_messages(mcpc_server_t *server)
"omit for the first page.",
MCPC_U8STR,
false)
- && tiny_mcp_tool_add_property(tool,
- "include_deleted",
- "When true, export view includes deleted records as DEL "
- "lines; other views ignore this option.",
- MCPC_U8STR,
- false)
&& mcpc_server_add_tool(server, tool) == MCPC_EC_0;
}