Skip to content

Commit d6768ad

Browse files
committed
refactor: simplify BM MCP JSON handling
1 parent 9b5fa89 commit d6768ad

File tree

4 files changed

+18
-80
lines changed

4 files changed

+18
-80
lines changed

bm-client.test.ts

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { beforeEach, describe, expect, it, jest } from "bun:test"
2-
import { BmClient, stripFrontmatter } from "./bm-client.ts"
2+
import { BmClient } from "./bm-client.ts"
33

44
function mcpResult(payload: unknown) {
55
return {
6-
structuredContent: payload,
6+
structuredContent: { result: payload },
77
content: [
88
{
99
type: "text",
@@ -23,14 +23,6 @@ function setConnected(client: BmClient, callTool: jest.Mock) {
2323
}
2424
}
2525

26-
describe("BmClient utility functions", () => {
27-
it("strips YAML frontmatter from content", () => {
28-
const content =
29-
"---\ntitle: Test Note\ndate: 2025-02-08\n---\n\nThis is the actual content"
30-
expect(stripFrontmatter(content)).toBe("This is the actual content")
31-
})
32-
})
33-
3426
describe("BmClient MCP behavior", () => {
3527
let client: BmClient
3628

@@ -173,14 +165,14 @@ describe("BmClient MCP behavior", () => {
173165
query: "marketing strategy",
174166
page: 1,
175167
page_size: 3,
176-
output_format: "default",
168+
output_format: "json",
177169
},
178170
})
179171
expect(results).toHaveLength(1)
180172
expect(results[0].title).toBe("x")
181173
})
182174

183-
it("buildContext calls build_context using json format", async () => {
175+
it("buildContext calls build_context using output_format=json", async () => {
184176
const callTool = jest.fn().mockResolvedValue(
185177
mcpResult({
186178
results: [
@@ -206,7 +198,7 @@ describe("BmClient MCP behavior", () => {
206198
arguments: {
207199
url: "memory://notes/x",
208200
depth: 2,
209-
format: "json",
201+
output_format: "json",
210202
},
211203
})
212204
expect(ctx.results).toHaveLength(1)

bm-client.ts

Lines changed: 9 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@ const REQUIRED_TOOLS = [
1919
"move_note",
2020
]
2121

22-
export function stripFrontmatter(content: string): string {
23-
return content.replace(/^---\n[\s\S]*?\n---\n*/, "").trim()
24-
}
25-
2622
export interface SearchResult {
2723
title: string
2824
permalink: string
@@ -113,28 +109,6 @@ function extractTextFromContent(content: unknown): string {
113109
return textBlocks.join("\n").trim()
114110
}
115111

116-
function tryParseJson(text: string): unknown {
117-
try {
118-
return JSON.parse(text)
119-
} catch {
120-
// fall through
121-
}
122-
123-
const lines = text.split("\n")
124-
for (let i = 0; i < lines.length; i++) {
125-
const trimmed = lines[i].trimStart()
126-
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) continue
127-
128-
try {
129-
return JSON.parse(lines.slice(i).join("\n"))
130-
} catch {
131-
// keep scanning
132-
}
133-
}
134-
135-
return text
136-
}
137-
138112
function isRecoverableConnectionError(err: unknown): boolean {
139113
const msg = getErrorMessage(err).toLowerCase()
140114
return (
@@ -404,28 +378,16 @@ export class BmClient {
404378
): Promise<unknown> {
405379
const result = await this.callToolRaw(name, args)
406380

407-
if (isRecord(result) && result.structuredContent !== undefined) {
408-
if (
409-
isRecord(result.structuredContent) &&
410-
result.structuredContent.result !== undefined
411-
) {
412-
return result.structuredContent.result
413-
}
414-
return result.structuredContent
415-
}
416-
417-
if (isRecord(result) && result.toolResult !== undefined) {
418-
return result.toolResult
381+
if (!isRecord(result) || result.structuredContent === undefined) {
382+
throw new Error(`BM MCP tool ${name} returned no structured payload`)
419383
}
420384

421-
if (isRecord(result) && result.content !== undefined) {
422-
const text = extractTextFromContent(result.content)
423-
if (text.length > 0) {
424-
return tryParseJson(text)
425-
}
385+
const structuredPayload = result.structuredContent
386+
if (isRecord(structuredPayload) && structuredPayload.result !== undefined) {
387+
return structuredPayload.result
426388
}
427389

428-
throw new Error(`BM MCP tool ${name} returned no usable payload`)
390+
return structuredPayload
429391
}
430392

431393
async ensureProject(projectPath: string): Promise<void> {
@@ -446,10 +408,6 @@ export class BmClient {
446408
output_format: "json",
447409
})
448410

449-
if (Array.isArray(payload)) {
450-
return payload as ProjectListResult[]
451-
}
452-
453411
if (isRecord(payload) && Array.isArray(payload.projects)) {
454412
return payload.projects as ProjectListResult[]
455413
}
@@ -462,13 +420,9 @@ export class BmClient {
462420
query,
463421
page: 1,
464422
page_size: limit,
465-
output_format: "default",
423+
output_format: "json",
466424
})
467425

468-
if (typeof payload === "string") {
469-
throw new Error(payload)
470-
}
471-
472426
if (!isRecord(payload) || !Array.isArray(payload.results)) {
473427
throw new Error("invalid search_notes response")
474428
}
@@ -504,11 +458,7 @@ export class BmClient {
504458
permalink,
505459
content,
506460
file_path: filePath,
507-
frontmatter: isRecord(payload.frontmatter)
508-
? payload.frontmatter
509-
: payload.frontmatter === null
510-
? null
511-
: null,
461+
frontmatter: isRecord(payload.frontmatter) ? payload.frontmatter : null,
512462
}
513463
}
514464

@@ -553,7 +503,7 @@ export class BmClient {
553503
const payload = await this.callTool("build_context", {
554504
url,
555505
depth,
556-
format: "json",
506+
output_format: "json",
557507
})
558508

559509
if (!isRecord(payload) || !Array.isArray(payload.results)) {
@@ -573,10 +523,6 @@ export class BmClient {
573523
return payload as RecentResult[]
574524
}
575525

576-
if (isRecord(payload) && Array.isArray(payload.items)) {
577-
return payload.items as RecentResult[]
578-
}
579-
580526
throw new Error("invalid recent_activity response")
581527
}
582528

tools/memory-provider.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ async function searchActiveTasks(
8181
for (const r of results) {
8282
const score = r.score ? ` (${r.score.toFixed(2)})` : ""
8383
const preview =
84-
(r.content ?? "").length > 200 ? `${(r.content ?? "").slice(0, 200)}…` : (r.content ?? "")
84+
(r.content ?? "").length > 200
85+
? `${(r.content ?? "").slice(0, 200)}…`
86+
: (r.content ?? "")
8587
matches.push(
8688
`- **${r.title}**${score}${r.file_path}\n > ${preview.replace(/\n/g, "\n > ")}`,
8789
)

tools/search.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,7 @@ export function registerSearchTool(
4646
const score = r.score ? ` (${(r.score * 100).toFixed(0)}%)` : ""
4747
const content = r.content ?? ""
4848
const preview =
49-
content.length > 200
50-
? `${content.slice(0, 200)}...`
51-
: content
49+
content.length > 200 ? `${content.slice(0, 200)}...` : content
5250
return `${i + 1}. **${r.title}**${score}\n ${preview}`
5351
})
5452
.join("\n\n")

0 commit comments

Comments
 (0)