summaryrefslogtreecommitdiff
path: root/src/mcp/tools/write_message.c
diff options
context:
space:
mode:
authortanaka takashi <isazaka@gmail.com>2026-07-04 14:54:45 +0900
committertanaka takashi <isazaka@gmail.com>2026-07-04 14:54:45 +0900
commit1efecaef1f867cb218366e2b4ab4b901fdd2835f (patch)
tree3080a3d80a1a0671e8e82819d3f785465aace162 /src/mcp/tools/write_message.c
parent34e00404e9561dcaf8d2d99df7470757a4fee915 (diff)
途中経過コミット
Diffstat (limited to 'src/mcp/tools/write_message.c')
-rw-r--r--src/mcp/tools/write_message.c212
1 files changed, 188 insertions, 24 deletions
diff --git a/src/mcp/tools/write_message.c b/src/mcp/tools/write_message.c
index 7df0058..3d7cb29 100644
--- a/src/mcp/tools/write_message.c
+++ b/src/mcp/tools/write_message.c
@@ -34,7 +34,9 @@ metadata_has_only_known_fields(const char *json, int length)
}
key[decoded] = '\0';
if (strcmp(key, "tool_call_id") != 0 && strcmp(key, "tags") != 0
- && strcmp(key, "memory_tier") != 0)
+ && strcmp(key, "memory_tier") != 0 && strcmp(key, "memory_kind") != 0
+ && strcmp(key, "importance") != 0 && strcmp(key, "summary") != 0
+ && strcmp(key, "relations") != 0 && strcmp(key, "source_message_ids") != 0)
{
return false;
}
@@ -42,14 +44,20 @@ metadata_has_only_known_fields(const char *json, int length)
return true;
}
-static bool
-parse_metadata(const char *json, tiny_message_t *message)
+bool
+tiny_mcp_parse_metadata(const char *json, tiny_message_t *message, tiny_metadata_mask_t *present)
{
- const char *tool_call = NULL, *tier = NULL, *tags = NULL;
- int tool_call_len = 0, tier_len = 0, tags_len = 0;
+ const char *tool_call = NULL, *tier = NULL, *tags = NULL, *kind = NULL, *summary = NULL;
+ const char *relations = NULL, *sources = NULL;
+ int tool_call_len = 0, tier_len = 0, tags_len = 0, kind_len = 0, summary_len = 0;
+ int relations_len = 0, sources_len = 0;
int offset = 0, key_offset, key_length, value_offset, value_length, value_type;
int length = (int)strlen(json);
int tool_call_type, tier_type, tags_type;
+ int kind_type, summary_type, importance_type;
+ int relations_type, sources_type;
+ double importance = 50;
+ tiny_metadata_mask_t fields = 0;
if (strcmp(json, "{}") == 0)
{
return true;
@@ -62,12 +70,176 @@ parse_metadata(const char *json, tiny_message_t *message)
tool_call_type = mjson_find(json, length, "$.tool_call_id", &tool_call, &tool_call_len);
tier_type = mjson_find(json, length, "$.memory_tier", &tier, &tier_len);
tags_type = mjson_find(json, length, "$.tags", &tags, &tags_len);
+ kind_type = mjson_find(json, length, "$.memory_kind", &kind, &kind_len);
+ summary_type = mjson_find(json, length, "$.summary", &summary, &summary_len);
+ importance_type = mjson_find(json, length, "$.importance", NULL, NULL);
+ relations_type = mjson_find(json, length, "$.relations", &relations, &relations_len);
+ sources_type = mjson_find(json, length, "$.source_message_ids", &sources, &sources_len);
if ((tool_call_type != MJSON_TOK_INVALID && tool_call_type != MJSON_TOK_STRING)
|| (tier_type != MJSON_TOK_INVALID && tier_type != MJSON_TOK_STRING)
- || (tags_type != MJSON_TOK_INVALID && tags_type != MJSON_TOK_ARRAY))
+ || (tags_type != MJSON_TOK_INVALID && tags_type != MJSON_TOK_ARRAY)
+ || (kind_type != MJSON_TOK_INVALID && kind_type != MJSON_TOK_STRING)
+ || (summary_type != MJSON_TOK_INVALID && summary_type != MJSON_TOK_STRING)
+ || (importance_type != MJSON_TOK_INVALID && importance_type != MJSON_TOK_NUMBER)
+ || (relations_type != MJSON_TOK_INVALID && relations_type != MJSON_TOK_ARRAY)
+ || (sources_type != MJSON_TOK_INVALID && sources_type != MJSON_TOK_ARRAY))
{
return false;
}
+ fields |= tool_call_type != MJSON_TOK_INVALID ? TINY_METADATA_TOOL_CALL_ID : 0;
+ fields |= tags_type != MJSON_TOK_INVALID ? TINY_METADATA_TAGS : 0;
+ fields |= tier_type != MJSON_TOK_INVALID ? TINY_METADATA_TIER : 0;
+ fields |= kind_type != MJSON_TOK_INVALID ? TINY_METADATA_KIND : 0;
+ fields |= importance_type != MJSON_TOK_INVALID ? TINY_METADATA_IMPORTANCE : 0;
+ fields |= summary_type != MJSON_TOK_INVALID ? TINY_METADATA_SUMMARY : 0;
+ fields |= relations_type != MJSON_TOK_INVALID ? TINY_METADATA_RELATIONS : 0;
+ fields |= sources_type != MJSON_TOK_INVALID ? TINY_METADATA_SOURCES : 0;
+ if (relations_type == MJSON_TOK_ARRAY)
+ {
+ offset = 0;
+ while ((offset = mjson_next(relations,
+ relations_len,
+ offset,
+ &key_offset,
+ &key_length,
+ &value_offset,
+ &value_length,
+ &value_type))
+ != 0)
+ {
+ tiny_relation_t relation;
+ char type[24];
+ int decoded_type, decoded_id;
+ tiny_relation_t *resized;
+ if (value_type != MJSON_TOK_OBJECT
+ || message->metadata.relation_count >= TINY_RELATION_COUNT_MAX)
+ {
+ return false;
+ }
+ decoded_type = mjson_get_string(
+ relations + value_offset, value_length, "$.type", type, sizeof(type));
+ decoded_id = mjson_get_string(relations + value_offset,
+ value_length,
+ "$.message_id",
+ relation.message_id,
+ sizeof(relation.message_id));
+ if (decoded_type <= 0 || decoded_type >= (int)sizeof(type)
+ || decoded_id != TINY_MESSAGE_ID_LENGTH)
+ {
+ return false;
+ }
+ type[decoded_type] = '\0';
+ relation.message_id[decoded_id] = '\0';
+ if (!tiny_relation_type_parse(type, &relation.type)
+ || !tiny_message_id_valid(relation.message_id))
+ {
+ return false;
+ }
+ resized = realloc(message->metadata.relations,
+ (message->metadata.relation_count + 1) * sizeof(*resized));
+ if (!resized)
+ {
+ return false;
+ }
+ message->metadata.relations = resized;
+ message->metadata.relations[message->metadata.relation_count++] = relation;
+ }
+ }
+ if (sources_type == MJSON_TOK_ARRAY)
+ {
+ offset = 0;
+ while ((offset = mjson_next(sources,
+ sources_len,
+ offset,
+ &key_offset,
+ &key_length,
+ &value_offset,
+ &value_length,
+ &value_type))
+ != 0)
+ {
+ char *id;
+ char **resized;
+ int decoded;
+ if (value_type != MJSON_TOK_STRING
+ || message->metadata.source_count >= TINY_SOURCE_COUNT_MAX)
+ {
+ return false;
+ }
+ id = malloc(TINY_MESSAGE_ID_LENGTH + 1);
+ if (!id)
+ {
+ return false;
+ }
+ decoded = mjson_get_string(
+ sources + value_offset, value_length, "$", id, TINY_MESSAGE_ID_LENGTH + 1);
+ if (decoded != TINY_MESSAGE_ID_LENGTH)
+ {
+ free(id);
+ return false;
+ }
+ id[decoded] = '\0';
+ if (!tiny_message_id_valid(id))
+ {
+ free(id);
+ return false;
+ }
+ resized = realloc(message->metadata.source_message_ids,
+ (message->metadata.source_count + 1) * sizeof(*resized));
+ if (!resized)
+ {
+ free(id);
+ return false;
+ }
+ message->metadata.source_message_ids = resized;
+ message->metadata.source_message_ids[message->metadata.source_count++] = id;
+ }
+ }
+ if (importance_type == MJSON_TOK_NUMBER
+ && mjson_get_number(json, length, "$.importance", &importance) > 0)
+ {
+ if (importance < 0 || importance > 100 || importance != (double)(uint8_t)importance)
+ {
+ return false;
+ }
+ message->metadata.importance = (uint8_t)importance;
+ }
+ if (kind_type == MJSON_TOK_STRING)
+ {
+ char value[24];
+ int decoded = mjson_get_string(json, length, "$.memory_kind", value, sizeof(value));
+ if (decoded <= 0 || decoded >= (int)sizeof(value))
+ {
+ return false;
+ }
+ value[decoded] = '\0';
+ if (!tiny_kind_parse(value, &message->metadata.memory_kind))
+ {
+ return false;
+ }
+ }
+ if (summary_type == MJSON_TOK_STRING)
+ {
+ int decoded;
+ message->metadata.summary = malloc(TINY_SUMMARY_MAX + 1);
+ if (!message->metadata.summary)
+ {
+ return false;
+ }
+ decoded = mjson_get_string(
+ json, length, "$.summary", message->metadata.summary, TINY_SUMMARY_MAX + 1);
+ if (decoded < 0 || decoded > TINY_SUMMARY_MAX
+ || (decoded && !tiny_utf8_valid(message->metadata.summary, (size_t)decoded)))
+ {
+ return false;
+ }
+ message->metadata.summary[decoded] = '\0';
+ if (decoded == 0)
+ {
+ free(message->metadata.summary);
+ message->metadata.summary = NULL;
+ }
+ }
if (tool_call_type == MJSON_TOK_STRING)
{
int decoded;
@@ -144,7 +316,11 @@ parse_metadata(const char *json, tiny_message_t *message)
message->metadata.tags[message->metadata.tag_count++] = tag;
}
}
- return true;
+ if (present)
+ {
+ *present = fields;
+ }
+ return tiny_metadata_valid(&message->metadata);
}
static void
@@ -163,7 +339,7 @@ call_write_message(const mcpc_tool_t *tool, mcpc_ucbr_t *result)
metadata = tiny_mcp_tool_get_string(tool, "metadata_json", TINY_CONTENT_MAX);
bool valid = context && input.session_id && input.session_id[0] && role && input.content
&& input.content[0] && tiny_role_parse(role, &input.role)
- && parse_metadata(metadata ? metadata : "{}", &input);
+ && tiny_mcp_parse_metadata(metadata ? metadata : "{}", &input, NULL);
if (!valid)
{
tiny_mcp_tool_add_error(result, "VALIDATION_ERROR", "invalid write_message arguments");
@@ -178,21 +354,7 @@ call_write_message(const mcpc_tool_t *tool, mcpc_ucbr_t *result)
}
else
{
- if (context->search_index
- && !tiny_search_index_add(
- context->search_index, &written, tiny_store_entry_count(context->store) - 1))
- {
- tiny_search_index_free(context->search_index);
- context->search_index = NULL;
- }
- if (context->history_index
- && !tiny_history_index_add(context->history_index,
- context->store,
- tiny_store_entry_count(context->store) - 1))
- {
- tiny_history_index_free(context->history_index);
- context->history_index = NULL;
- }
+ tiny_mcp_tools_invalidate_indexes(context);
tiny_json_init(&json);
tiny_json_raw(&json, "{\"status\":\"OK\",\"message\":");
tiny_mcp_tool_append_message(&json, &written);
@@ -225,7 +387,9 @@ tiny_mcp_register_write_message(mcpc_server_t *server)
&& tiny_mcp_tool_add_property(tool,
"metadata_json",
"Optional JSON object string containing tool_call_id, tags, "
- "and/or memory_tier; defaults to {}.",
+ "memory_tier, memory_kind, importance, summary, relations, "
+ "and/or source_message_ids; "
+ "defaults to {}.",
MCPC_U8STR,
false)
&& mcpc_server_add_tool(server, tool) == MCPC_EC_0;