mcp-sampling.test.mjs
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