Skip to content

Commit 25f15e5

Browse files
committed
refactor(mcp): update formatters to return structured {content, tokens}
- Remove token footer from content string (moved to metadata) - Return tokens as separate field in FormattedResult - Update formatter tests for new structure Part of #51
1 parent bbe0379 commit 25f15e5

File tree

3 files changed

+30
-37
lines changed

3 files changed

+30
-37
lines changed

packages/mcp-server/src/formatters/__tests__/formatters.test.ts

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ describe('Formatters', () => {
6969
expect(result.content).toContain('1. [89%]');
7070
expect(result.content).toContain('2. [84%]');
7171
expect(result.content).toContain('3. [72%]');
72-
expect(result.tokenEstimate).toBeGreaterThan(0);
73-
// Should include token footer
74-
expect(result.content).toMatch(/🪙 ~\d+ tokens$/);
72+
expect(result.tokens).toBeGreaterThan(0);
73+
// Token footer moved to metadata, no longer in content
74+
expect(result.content).not.toContain('🪙');
7575
});
7676

7777
it('should respect maxResults option', () => {
@@ -143,8 +143,9 @@ describe('Formatters', () => {
143143
// Should have double newlines between results
144144
expect(result.content).toContain('\n\n');
145145

146-
// Should include token footer
147-
expect(result.content).toMatch(/🪙 ~\d+ tokens$/);
146+
// Token footer moved to metadata, no longer in content
147+
expect(result.content).not.toContain('🪙');
148+
expect(result.tokens).toBeGreaterThan(0);
148149
});
149150

150151
it('should include signatures by default', () => {
@@ -209,7 +210,7 @@ describe('Formatters', () => {
209210
const verboseResult = verboseFormatter.formatResults(mockResults);
210211

211212
// Verbose should be significantly larger
212-
expect(verboseResult.tokenEstimate).toBeGreaterThan(compactResult.tokenEstimate * 2);
213+
expect(verboseResult.tokens).toBeGreaterThan(compactResult.tokens * 2);
213214
});
214215

215216
it('token estimates should scale with result count', () => {
@@ -218,37 +219,35 @@ describe('Formatters', () => {
218219
const oneResult = formatter.formatResults([mockResults[0]]);
219220
const threeResults = formatter.formatResults(mockResults);
220221

221-
expect(threeResults.tokenEstimate).toBeGreaterThan(oneResult.tokenEstimate * 2);
222+
expect(threeResults.tokens).toBeGreaterThan(oneResult.tokens * 2);
222223
});
223224
});
224225

225-
describe('Token Footer', () => {
226-
it('compact formatter should include coin emoji footer', () => {
226+
describe('Structured Metadata', () => {
227+
it('compact formatter should return tokens in result object', () => {
227228
const formatter = new CompactFormatter();
228229
const result = formatter.formatResults(mockResults);
229230

230-
expect(result.content).toContain('🪙');
231-
expect(result.content).toMatch(/~\d+ tokens$/);
231+
// Token info is now in the result object, not the content
232+
expect(result.tokens).toBeGreaterThan(0);
233+
expect(result.content).not.toContain('🪙');
232234
});
233235

234-
it('verbose formatter should include coin emoji footer', () => {
236+
it('verbose formatter should return tokens in result object', () => {
235237
const formatter = new VerboseFormatter();
236238
const result = formatter.formatResults(mockResults);
237239

238-
expect(result.content).toContain('🪙');
239-
expect(result.content).toMatch(/~\d+ tokens$/);
240+
// Token info is now in the result object, not the content
241+
expect(result.tokens).toBeGreaterThan(0);
242+
expect(result.content).not.toContain('🪙');
240243
});
241244

242-
it('token footer should match tokenEstimate property', () => {
245+
it('tokens property should be a positive number', () => {
243246
const formatter = new CompactFormatter();
244247
const result = formatter.formatResults(mockResults);
245248

246-
// Extract token count from footer
247-
const footerMatch = result.content.match(/🪙 ~(\d+) tokens$/);
248-
expect(footerMatch).toBeTruthy();
249-
250-
const footerTokens = Number.parseInt(footerMatch?.[1] ?? '0', 10);
251-
expect(footerTokens).toBe(result.tokenEstimate);
249+
expect(typeof result.tokens).toBe('number');
250+
expect(result.tokens).toBeGreaterThan(0);
252251
});
253252
});
254253
});

packages/mcp-server/src/formatters/compact-formatter.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export class CompactFormatter implements ResultFormatter {
5959
const content = 'No results found';
6060
return {
6161
content,
62-
tokenEstimate: estimateTokensForText(content),
62+
tokens: estimateTokensForText(content),
6363
};
6464
}
6565

@@ -71,16 +71,13 @@ export class CompactFormatter implements ResultFormatter {
7171
return `${index + 1}. ${this.formatResult(result)}`;
7272
});
7373

74-
// Calculate total tokens
75-
const contentLines = formatted.join('\n');
76-
const tokenEstimate = estimateTokensForText(contentLines);
77-
78-
// Add token footer
79-
const content = `${contentLines}\n\n🪙 ~${tokenEstimate} tokens`;
74+
// Calculate total tokens (content only, no footer)
75+
const content = formatted.join('\n');
76+
const tokens = estimateTokensForText(content);
8077

8178
return {
8279
content,
83-
tokenEstimate,
80+
tokens,
8481
};
8582
}
8683

packages/mcp-server/src/formatters/verbose-formatter.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export class VerboseFormatter implements ResultFormatter {
8989
const content = 'No results found';
9090
return {
9191
content,
92-
tokenEstimate: estimateTokensForText(content),
92+
tokens: estimateTokensForText(content),
9393
};
9494
}
9595

@@ -101,16 +101,13 @@ export class VerboseFormatter implements ResultFormatter {
101101
return `${index + 1}. ${this.formatResult(result)}`;
102102
});
103103

104-
// Calculate total tokens
105-
const contentLines = formatted.join('\n\n'); // Double newline for separation
106-
const tokenEstimate = estimateTokensForText(contentLines);
107-
108-
// Add token footer
109-
const content = `${contentLines}\n\n🪙 ~${tokenEstimate} tokens`;
104+
// Calculate total tokens (content only, no footer)
105+
const content = formatted.join('\n\n'); // Double newline for separation
106+
const tokens = estimateTokensForText(content);
110107

111108
return {
112109
content,
113-
tokenEstimate,
110+
tokens,
114111
};
115112
}
116113

0 commit comments

Comments
 (0)