mcp-sampling.test.mjs
147 lines 4.9 KB
Raw
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor ⚠ breaking 1 day ago
1 import { describe, it, beforeEach } from 'node:test';
2 import assert from 'node:assert/strict';
3 import { trySampling, trySamplingJson, clientSupportsSampling, samplingResultToText } from '../mcp/sampling.mjs';
4
5 function makeMockServer({ sampling = true, createMessageResult = null, createMessageThrows = false } = {}) {
6 return {
7 server: {
8 getClientCapabilities: () => (sampling ? { sampling: {} } : {}),
9 createMessage: async (req) => {
10 if (createMessageThrows) throw new Error('sampling failed');
11 return createMessageResult ?? {
12 content: { type: 'text', text: `response to: ${req.messages[0]?.content?.text}` },
13 model: 'mock',
14 role: 'assistant',
15 };
16 },
17 },
18 };
19 }
20
21 describe('samplingResultToText', () => {
22 it('handles single text content', () => {
23 assert.equal(samplingResultToText({ content: { type: 'text', text: 'hello' } }), 'hello');
24 });
25 it('handles array content', () => {
26 assert.equal(
27 samplingResultToText({
28 content: [
29 { type: 'text', text: 'a' },
30 { type: 'text', text: 'b' },
31 ],
32 }),
33 'a\nb'
34 );
35 });
36 it('returns empty for null', () => {
37 assert.equal(samplingResultToText(null), '');
38 assert.equal(samplingResultToText({}), '');
39 assert.equal(samplingResultToText({ content: null }), '');
40 });
41 });
42
43 describe('clientSupportsSampling', () => {
44 it('returns true when sampling capability present', () => {
45 assert.equal(clientSupportsSampling(makeMockServer({ sampling: true })), true);
46 });
47 it('returns false when sampling capability absent', () => {
48 assert.equal(clientSupportsSampling(makeMockServer({ sampling: false })), false);
49 });
50 });
51
52 describe('trySampling', () => {
53 it('returns text when sampling available', async () => {
54 const result = await trySampling(makeMockServer(), { system: 'sys', user: 'hello', maxTokens: 100 });
55 assert.equal(result, 'response to: hello');
56 });
57
58 it('returns null when sampling not available', async () => {
59 const result = await trySampling(makeMockServer({ sampling: false }), { system: 'sys', user: 'hello' });
60 assert.equal(result, null);
61 });
62
63 it('returns null on createMessage error', async () => {
64 const result = await trySampling(
65 makeMockServer({ createMessageThrows: true }),
66 { system: 'sys', user: 'hello' }
67 );
68 assert.equal(result, null);
69 });
70
71 it('returns null for empty response', async () => {
72 const result = await trySampling(
73 makeMockServer({ createMessageResult: { content: { type: 'text', text: '' } } }),
74 { system: 'sys', user: 'hello' }
75 );
76 assert.equal(result, null);
77 });
78
79 it('clamps maxTokens to 1-8192', async () => {
80 let capturedReq = null;
81 const server = {
82 server: {
83 getClientCapabilities: () => ({ sampling: {} }),
84 createMessage: async (req) => {
85 capturedReq = req;
86 return { content: { type: 'text', text: 'ok' } };
87 },
88 },
89 };
90 await trySampling(server, { system: 'sys', user: 'u', maxTokens: 99999 });
91 assert.equal(capturedReq.maxTokens, 8192);
92 await trySampling(server, { system: 'sys', user: 'u', maxTokens: -5 });
93 assert.equal(capturedReq.maxTokens, 1);
94 });
95
96 it('defaults maxTokens to 512', async () => {
97 let capturedReq = null;
98 const server = {
99 server: {
100 getClientCapabilities: () => ({ sampling: {} }),
101 createMessage: async (req) => {
102 capturedReq = req;
103 return { content: { type: 'text', text: 'ok' } };
104 },
105 },
106 };
107 await trySampling(server, { system: 'sys', user: 'u' });
108 assert.equal(capturedReq.maxTokens, 512);
109 });
110 });
111
112 describe('trySamplingJson', () => {
113 it('parses valid JSON from sampling response', async () => {
114 const server = makeMockServer({
115 createMessageResult: {
116 content: { type: 'text', text: '{"project":"test","tags":["a","b"]}' },
117 },
118 });
119 const result = await trySamplingJson(server, { system: 'sys', user: 'u' });
120 assert.deepEqual(result, { project: 'test', tags: ['a', 'b'] });
121 });
122
123 it('strips markdown fences from JSON', async () => {
124 const server = makeMockServer({
125 createMessageResult: {
126 content: { type: 'text', text: '```json\n{"ok": true}\n```' },
127 },
128 });
129 const result = await trySamplingJson(server, { system: 'sys', user: 'u' });
130 assert.deepEqual(result, { ok: true });
131 });
132
133 it('returns null for invalid JSON', async () => {
134 const server = makeMockServer({
135 createMessageResult: {
136 content: { type: 'text', text: 'not valid json at all' },
137 },
138 });
139 const result = await trySamplingJson(server, { system: 'sys', user: 'u' });
140 assert.equal(result, null);
141 });
142
143 it('returns null when sampling unavailable', async () => {
144 const result = await trySamplingJson(makeMockServer({ sampling: false }), { system: 'sys', user: 'u' });
145 assert.equal(result, null);
146 });
147 });
File History 2 commits
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor 1 day ago
sha256:9103f98c89257ed2b01c237cea895dabb3e85ea337dccb1161c175e4422355b6 docs: accept Calendar Events v0 spec with Phase 0 security … Human 1 day ago