@gethmy/mcp 2.8.5 → 2.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +8 -6
- package/dist/index.js +8 -6
- package/dist/lib/api-client.js +4 -0
- package/package.json +1 -1
- package/src/api-client.ts +6 -0
- package/src/graph-expansion.ts +5 -7
- package/src/server.ts +16 -10
package/dist/cli.js
CHANGED
|
@@ -1720,6 +1720,10 @@ class HarmonyApiClient {
|
|
|
1720
1720
|
params.set("type", options.type);
|
|
1721
1721
|
if (options?.limit !== undefined)
|
|
1722
1722
|
params.set("limit", String(options.limit));
|
|
1723
|
+
for (const tag of options?.tags ?? [])
|
|
1724
|
+
params.append("tags", tag);
|
|
1725
|
+
if (options?.include_superseded)
|
|
1726
|
+
params.set("include_superseded", "true");
|
|
1723
1727
|
return this.request("GET", `/memory/search?${params.toString()}`);
|
|
1724
1728
|
}
|
|
1725
1729
|
async getVaultIndex(options) {
|
|
@@ -5329,23 +5333,21 @@ async function handleToolCall(name, args, deps) {
|
|
|
5329
5333
|
let entities;
|
|
5330
5334
|
let relevanceMap;
|
|
5331
5335
|
if (queryText) {
|
|
5336
|
+
const requestedTags = args.tags;
|
|
5332
5337
|
const searchResult = await client3.searchMemoryEntities(workspaceId, queryText, {
|
|
5333
5338
|
project_id: projectId,
|
|
5334
5339
|
type: args.type,
|
|
5335
|
-
limit: fetchLimit
|
|
5340
|
+
limit: fetchLimit,
|
|
5341
|
+
tags: requestedTags && requestedTags.length > 0 ? requestedTags : undefined,
|
|
5342
|
+
include_superseded: includeSuperseded
|
|
5336
5343
|
});
|
|
5337
5344
|
entities = searchResult.entities ?? [];
|
|
5338
|
-
const requestedTags = args.tags;
|
|
5339
5345
|
const minConfidence = args.minConfidence;
|
|
5340
5346
|
if (userScopeFilter) {
|
|
5341
5347
|
entities = entities.filter((e) => e?.scope === userScopeFilter);
|
|
5342
5348
|
} else if (excludeSessionFromLongTerm) {
|
|
5343
5349
|
entities = entities.filter((e) => !isSessionScope(e?.scope));
|
|
5344
5350
|
}
|
|
5345
|
-
if (requestedTags && requestedTags.length > 0) {
|
|
5346
|
-
const wanted = new Set(requestedTags);
|
|
5347
|
-
entities = entities.filter((e) => (e?.tags ?? []).some((t) => wanted.has(t)));
|
|
5348
|
-
}
|
|
5349
5351
|
entities = filterByMinConfidence(entities, minConfidence);
|
|
5350
5352
|
if (!includeSuperseded) {
|
|
5351
5353
|
entities = entities.filter((e) => !e?.superseded_at);
|
package/dist/index.js
CHANGED
|
@@ -1716,6 +1716,10 @@ class HarmonyApiClient {
|
|
|
1716
1716
|
params.set("type", options.type);
|
|
1717
1717
|
if (options?.limit !== undefined)
|
|
1718
1718
|
params.set("limit", String(options.limit));
|
|
1719
|
+
for (const tag of options?.tags ?? [])
|
|
1720
|
+
params.append("tags", tag);
|
|
1721
|
+
if (options?.include_superseded)
|
|
1722
|
+
params.set("include_superseded", "true");
|
|
1719
1723
|
return this.request("GET", `/memory/search?${params.toString()}`);
|
|
1720
1724
|
}
|
|
1721
1725
|
async getVaultIndex(options) {
|
|
@@ -5325,23 +5329,21 @@ async function handleToolCall(name, args, deps) {
|
|
|
5325
5329
|
let entities;
|
|
5326
5330
|
let relevanceMap;
|
|
5327
5331
|
if (queryText) {
|
|
5332
|
+
const requestedTags = args.tags;
|
|
5328
5333
|
const searchResult = await client3.searchMemoryEntities(workspaceId, queryText, {
|
|
5329
5334
|
project_id: projectId,
|
|
5330
5335
|
type: args.type,
|
|
5331
|
-
limit: fetchLimit
|
|
5336
|
+
limit: fetchLimit,
|
|
5337
|
+
tags: requestedTags && requestedTags.length > 0 ? requestedTags : undefined,
|
|
5338
|
+
include_superseded: includeSuperseded
|
|
5332
5339
|
});
|
|
5333
5340
|
entities = searchResult.entities ?? [];
|
|
5334
|
-
const requestedTags = args.tags;
|
|
5335
5341
|
const minConfidence = args.minConfidence;
|
|
5336
5342
|
if (userScopeFilter) {
|
|
5337
5343
|
entities = entities.filter((e) => e?.scope === userScopeFilter);
|
|
5338
5344
|
} else if (excludeSessionFromLongTerm) {
|
|
5339
5345
|
entities = entities.filter((e) => !isSessionScope(e?.scope));
|
|
5340
5346
|
}
|
|
5341
|
-
if (requestedTags && requestedTags.length > 0) {
|
|
5342
|
-
const wanted = new Set(requestedTags);
|
|
5343
|
-
entities = entities.filter((e) => (e?.tags ?? []).some((t) => wanted.has(t)));
|
|
5344
|
-
}
|
|
5345
5347
|
entities = filterByMinConfidence(entities, minConfidence);
|
|
5346
5348
|
if (!includeSuperseded) {
|
|
5347
5349
|
entities = entities.filter((e) => !e?.superseded_at);
|
package/dist/lib/api-client.js
CHANGED
|
@@ -1323,6 +1323,10 @@ class HarmonyApiClient {
|
|
|
1323
1323
|
params.set("type", options.type);
|
|
1324
1324
|
if (options?.limit !== undefined)
|
|
1325
1325
|
params.set("limit", String(options.limit));
|
|
1326
|
+
for (const tag of options?.tags ?? [])
|
|
1327
|
+
params.append("tags", tag);
|
|
1328
|
+
if (options?.include_superseded)
|
|
1329
|
+
params.set("include_superseded", "true");
|
|
1326
1330
|
return this.request("GET", `/memory/search?${params.toString()}`);
|
|
1327
1331
|
}
|
|
1328
1332
|
async getVaultIndex(options) {
|
package/package.json
CHANGED
package/src/api-client.ts
CHANGED
|
@@ -1003,6 +1003,8 @@ export class HarmonyApiClient {
|
|
|
1003
1003
|
project_id?: string;
|
|
1004
1004
|
type?: string;
|
|
1005
1005
|
limit?: number;
|
|
1006
|
+
tags?: string[];
|
|
1007
|
+
include_superseded?: boolean;
|
|
1006
1008
|
},
|
|
1007
1009
|
): Promise<{ entities: unknown[]; count: number }> {
|
|
1008
1010
|
const params = new URLSearchParams();
|
|
@@ -1012,6 +1014,10 @@ export class HarmonyApiClient {
|
|
|
1012
1014
|
if (options?.type) params.set("type", options.type);
|
|
1013
1015
|
if (options?.limit !== undefined)
|
|
1014
1016
|
params.set("limit", String(options.limit));
|
|
1017
|
+
// Repeated `tags` params — the search endpoint reads them via getAll and
|
|
1018
|
+
// matches against the canonical `tags_normalized` column (#299).
|
|
1019
|
+
for (const tag of options?.tags ?? []) params.append("tags", tag);
|
|
1020
|
+
if (options?.include_superseded) params.set("include_superseded", "true");
|
|
1015
1021
|
return this.request("GET", `/memory/search?${params.toString()}`);
|
|
1016
1022
|
}
|
|
1017
1023
|
|
package/src/graph-expansion.ts
CHANGED
|
@@ -264,13 +264,11 @@ export async function findSupersedeCandidates(
|
|
|
264
264
|
if (options?.scope && (e as { scope?: string }).scope !== undefined) {
|
|
265
265
|
if ((e as { scope?: string }).scope !== options.scope) return false;
|
|
266
266
|
}
|
|
267
|
-
// Skip already-superseded rows
|
|
268
|
-
//
|
|
269
|
-
//
|
|
270
|
-
//
|
|
271
|
-
//
|
|
272
|
-
// to supersede. A complete fix needs the RPC to return/filter the column
|
|
273
|
-
// (migration + deploy); tracked in docs/memory.md.
|
|
267
|
+
// Skip already-superseded rows. The hybrid-search RPC now both returns
|
|
268
|
+
// `superseded_at` and excludes tombstoned rows by default (#298), so
|
|
269
|
+
// retired rows no longer surface as candidates on the embedding path.
|
|
270
|
+
// Kept as belt-and-suspenders for the FTS fallback and any caller that
|
|
271
|
+
// opts into include_superseded.
|
|
274
272
|
if ((e as { superseded_at?: string | null }).superseded_at) {
|
|
275
273
|
return false;
|
|
276
274
|
}
|
package/src/server.ts
CHANGED
|
@@ -3080,6 +3080,12 @@ async function handleToolCall(
|
|
|
3080
3080
|
let relevanceMap: Map<string, number>;
|
|
3081
3081
|
|
|
3082
3082
|
if (queryText) {
|
|
3083
|
+
const requestedTags = args.tags as string[] | undefined;
|
|
3084
|
+
// Tag + superseded filtering is now authoritative in the hybrid_search
|
|
3085
|
+
// RPC (#298/#299): tags match the canonical `tags_normalized` column at
|
|
3086
|
+
// the DB level (no client-side fetch-then-filter completeness gap), and
|
|
3087
|
+
// tombstoned rows are excluded unless include_superseded is set. Tags
|
|
3088
|
+
// are normalized server-side; we pass them through verbatim.
|
|
3083
3089
|
const searchResult = await client.searchMemoryEntities(
|
|
3084
3090
|
workspaceId,
|
|
3085
3091
|
queryText,
|
|
@@ -3087,14 +3093,18 @@ async function handleToolCall(
|
|
|
3087
3093
|
project_id: projectId,
|
|
3088
3094
|
type: args.type as string | undefined,
|
|
3089
3095
|
limit: fetchLimit,
|
|
3096
|
+
tags:
|
|
3097
|
+
requestedTags && requestedTags.length > 0
|
|
3098
|
+
? requestedTags
|
|
3099
|
+
: undefined,
|
|
3100
|
+
include_superseded: includeSuperseded,
|
|
3090
3101
|
},
|
|
3091
3102
|
);
|
|
3092
3103
|
entities = (searchResult.entities ?? []) as any[];
|
|
3093
3104
|
|
|
3094
|
-
// Post-filter the
|
|
3095
|
-
//
|
|
3096
|
-
//
|
|
3097
|
-
const requestedTags = args.tags as string[] | undefined;
|
|
3105
|
+
// Post-filter the params the RPC does not handle. Scope + minConfidence
|
|
3106
|
+
// are applied here on the rank-ordered set. (Tags + include_superseded
|
|
3107
|
+
// are handled server-side above.)
|
|
3098
3108
|
const minConfidence = args.minConfidence as number | undefined;
|
|
3099
3109
|
if (userScopeFilter) {
|
|
3100
3110
|
entities = entities.filter((e) => e?.scope === userScopeFilter);
|
|
@@ -3103,13 +3113,9 @@ async function handleToolCall(
|
|
|
3103
3113
|
// out of the long-term mix so the same row never shows twice.
|
|
3104
3114
|
entities = entities.filter((e) => !isSessionScope(e?.scope));
|
|
3105
3115
|
}
|
|
3106
|
-
if (requestedTags && requestedTags.length > 0) {
|
|
3107
|
-
const wanted = new Set(requestedTags);
|
|
3108
|
-
entities = entities.filter((e) =>
|
|
3109
|
-
(e?.tags ?? []).some((t: string) => wanted.has(t)),
|
|
3110
|
-
);
|
|
3111
|
-
}
|
|
3112
3116
|
entities = filterByMinConfidence(entities, minConfidence);
|
|
3117
|
+
// Belt-and-suspenders: the RPC already excludes tombstoned rows; this
|
|
3118
|
+
// also drops any if a stale/FTS path slipped one through.
|
|
3113
3119
|
if (!includeSuperseded) {
|
|
3114
3120
|
entities = entities.filter((e) => !e?.superseded_at);
|
|
3115
3121
|
}
|