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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
#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 persisted message content and tags by text relevance. Use it to "
"recall matching information globally or within one session.",
call_search_messages);
return tool
&& tiny_mcp_tool_add_property(
tool,
"query",
"UTF-8 text to match against persisted message content and tags.",
MCPC_U8STR,
true)
&& tiny_mcp_tool_add_property(
tool,
"session_id",
"Conversation session to search; omit to search across all sessions.",
MCPC_U8STR,
false)
&& tiny_mcp_tool_add_property(
tool,
"limit",
"Maximum ranked matches to return, from 1 to 100; defaults to 20.",
MCPC_I32,
false)
&& mcpc_server_add_tool(server, tool) == MCPC_EC_0;
}
|