1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
#include "mcp/tools/common/registry.h"
#include "mcp/tools/common/common.h"
#include "mcp/tools/common/context.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "base/allocator.h"
typedef struct result_item
{
const tiny_store_entry_t *entry;
double score;
} result_item_t;
static int
compare_results(const void *left, const void *right)
{
const result_item_t *a = left;
const result_item_t *b = right;
if (a->score > b->score)
{
return -1;
}
if (a->score < b->score)
{
return 1;
}
if (a->entry->message.timestamp_ms > b->entry->message.timestamp_ms)
{
return -1;
}
if (a->entry->message.timestamp_ms < b->entry->message.timestamp_ms)
{
return 1;
}
return strcmp(a->entry->message.message_id, b->entry->message.message_id);
}
static void
call_search_messages(const mcpc_tool_t *tool, mcpc_ucbr_t *result)
{
tiny_mcp_tools_context_t *context = tiny_mcp_tools_context_current();
char *query = tiny_mcp_tool_get_string(tool, "query", TINY_QUERY_MAX);
char *session = tiny_mcp_tool_get_string(tool, "session_id", TINY_SESSION_ID_MAX);
int32_t requested_limit = 20;
mcpc_errcode_t limit_status
= mcpc_tool_get_tpropval_i32(tool, (const char8_t *)"limit", &requested_limit);
size_t limit, index, count = 0, candidate_count = 0, *candidates = NULL;
result_item_t *items = NULL;
tiny_json_buffer_t json;
bool valid = context && query && query[0]
&& (limit_status == MCPC_EC_0 || limit_status == MCPC_EC_ANYDATA_NOTFOUND)
&& requested_limit >= 1 && requested_limit <= 100;
if (!valid)
{
tiny_mcp_tool_add_error(result, "VALIDATION_ERROR", "query or limit is invalid");
}
else
{
limit = (size_t)requested_limit;
if (!context->search_index)
{
context->search_index = tiny_mcp_tools_build_search_index(context->store);
}
if (!context->search_index
|| !tiny_search_index_candidates(
context->search_index, query, &candidates, &candidate_count))
{
tiny_mcp_tool_add_error(result, "INTERNAL_ERROR", "search index allocation failed");
}
else
{
items = malloc((candidate_count + 1) * sizeof(*items));
if (!items)
{
tiny_mcp_tool_add_error(result, "INTERNAL_ERROR", "out of memory");
}
else
{
for (index = 0; index < candidate_count; ++index)
{
const tiny_store_entry_t *entry
= tiny_store_entry(context->store, candidates[index]);
double score;
if (!entry || entry->deleted
|| (session && session[0] && strcmp(entry->message.session_id, session) != 0))
{
continue;
}
score = tiny_search_score(query, &entry->message);
if (score > 0)
{
items[count].entry = entry;
items[count++].score = score;
}
}
qsort(items, count, sizeof(*items), compare_results);
if (count > limit)
{
count = limit;
}
tiny_json_init(&json);
tiny_json_raw(&json, "{\"results\":[");
for (index = 0; index < count; ++index)
{
if (index)
{
tiny_json_raw(&json, ",");
}
tiny_mcp_tool_append_message(&json, &items[index].entry->message);
}
tiny_json_raw(&json, "]}");
tiny_mcp_tool_add_json(result, &json);
tiny_json_free(&json);
}
}
}
free(items);
free(candidates);
free(query);
free(session);
}
bool
tiny_mcp_register_search_messages(mcpc_server_t *server)
{
mcpc_tool_t *tool = tiny_mcp_tool_new("search_messages", "Search messages", call_search_messages);
return tool && tiny_mcp_tool_add_property(tool, "query", "Search text", MCPC_U8STR, true)
&& tiny_mcp_tool_add_property(
tool, "session_id", "Optional session ID; omitted searches all", MCPC_U8STR, false)
&& tiny_mcp_tool_add_property(
tool, "limit", "Optional result limit from 1 to 100; defaults to 20", MCPC_I32, false)
&& mcpc_server_add_tool(server, tool) == MCPC_EC_0;
}
|