Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
6e81e86
initial agent suggested changes + refactoring
CaitieM20 Mar 13, 2026
c481e98
initial agent suggested changes + refactoring
CaitieM20 Mar 13, 2026
ab655cb
add ListRoot Tests
CaitieM20 Mar 13, 2026
3d04a7c
Remove duplicate test
CaitieM20 Mar 13, 2026
b78e783
rename tests
CaitieM20 Mar 16, 2026
78032aa
update rawMCP connection logic to not use sessionId, remove unnecessa…
CaitieM20 Mar 17, 2026
b8621d4
update all tests to include all types of inputRequests
CaitieM20 Mar 17, 2026
85ef0af
removing duplicative tests
CaitieM20 Mar 17, 2026
9d86c97
removing duplicative tests
CaitieM20 Mar 17, 2026
d446a35
removing duplicative tests
CaitieM20 Mar 17, 2026
a8fa736
Fix formatting
CaitieM20 Mar 17, 2026
71d682a
Merge branch 'main' into mrtr-tests
CaitieM20 May 14, 2026
23226ed
Agent WIP
CaitieM20 May 15, 2026
1ae8855
fix formatting
CaitieM20 May 15, 2026
7324cba
remove session id and cleanup resultType Checks
CaitieM20 May 20, 2026
e203295
remove tasks
CaitieM20 May 20, 2026
d22b308
formatting
CaitieM20 May 20, 2026
5b5c02f
Merge branch 'main' into mrtr-tests
CaitieM20 May 20, 2026
c869c9a
merge main
CaitieM20 May 20, 2026
9e3f2d8
fix source missing issue
CaitieM20 May 20, 2026
038fef5
remove mcp-session-id
CaitieM20 May 20, 2026
73cbc59
fix linting issues
CaitieM20 May 20, 2026
76b8874
switch to using RPC
CaitieM20 May 20, 2026
d1ea13e
remove negative tests, returning a complete result is normal behavior.
CaitieM20 May 20, 2026
bd1dc47
update client tests
CaitieM20 May 20, 2026
742a37c
style checks
CaitieM20 May 20, 2026
7c7c21c
fixing CI issues
CaitieM20 May 20, 2026
e0c5fbf
Merge pull request #1 from CaitieM20/mrtr-tests-5-14-update
CaitieM20 May 20, 2026
a53faf2
revert client-helper.ts changes no longer needed
CaitieM20 May 20, 2026
f56d110
revert client-helper no longer needed with sessionless change
CaitieM20 May 20, 2026
e76270c
revert client-helper.ts
CaitieM20 May 20, 2026
774d53b
Apply suggestion from @CaitieM20
CaitieM20 May 20, 2026
a041f5c
Mrtr audit (#2)
CaitieM20 May 20, 2026
a5e971f
Mrtr tests updates (#3)
CaitieM20 May 21, 2026
756a125
fix bugs and align naming
CaitieM20 May 21, 2026
41140e3
don't hardcode port in negative test server
CaitieM20 May 21, 2026
f5a0d31
Merge branch 'main' into mrtr-tests
CaitieM20 May 21, 2026
8a6a671
fix merge
CaitieM20 May 21, 2026
b1f6223
Drop scenario-gate rows from sep-2322.yaml; exclude key-uniqueness re…
pcarleton May 22, 2026
85fe6b7
Require byte-exact requestState echo in MRTR client check
pcarleton May 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 143 additions & 0 deletions examples/clients/typescript/everything-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,149 @@ registerScenario(
runEnterpriseManagedAuthorization
);

// ============================================================================
// MRTR client conformance (SEP-2322)
// ============================================================================

async function runMRTRClient(serverUrl: string): Promise<void> {
let nextId = 1;

async function sendRpc(
method: string,
params?: Record<string, unknown>
): Promise<{
id: number;
result?: Record<string, unknown>;
error?: { code: number; message: string };
}> {
const id = nextId++;
const body: Record<string, unknown> = {
jsonrpc: '2.0',
id,
method
};
if (params) body.params = params;

const resp = await fetch(serverUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});

if (resp.status === 204) return { id, result: {} };
return (await resp.json()) as {
id: number;
result?: Record<string, unknown>;
error?: { code: number; message: string };
};
}

// List tools
const toolsResp = await sendRpc('tools/list');
const tools =
(toolsResp.result as { tools: Array<{ name: string }> })?.tools ?? [];
logger.debug(
'Available tools:',
tools.map((t) => t.name)
);

// Tool 1: test_mrtr_echo_state — call, get InputRequiredResult with requestState, retry
const r1 = await sendRpc('tools/call', {
name: 'test_mrtr_echo_state',
arguments: {}
});

const r1Result = r1.result as Record<string, unknown> | undefined;
if (r1Result?.resultType === 'input_required') {
const inputRequests = r1Result.inputRequests as Record<string, unknown>;
const requestState = r1Result.requestState as string | undefined;

// Build inputResponses by fulfilling each inputRequest
const inputResponses: Record<string, unknown> = {};
for (const [key, req] of Object.entries(inputRequests)) {
const request = req as { method: string; params: unknown };
if (request.method === 'elicitation/create') {
inputResponses[key] = {
action: 'accept',
content: { confirmed: true }
};
}
}

// Call an unrelated tool BEFORE retrying — must NOT carry over inputResponses/requestState
await sendRpc('tools/call', {
name: 'test_mrtr_unrelated',
arguments: {}
});
logger.debug(
'test_mrtr_unrelated: called without MRTR state (isolation check)'
);

// Retry with inputResponses + requestState echoed back unchanged
const retryParams: Record<string, unknown> = {
name: 'test_mrtr_echo_state',
arguments: {},
inputResponses
};
if (requestState !== undefined) {
retryParams.requestState = requestState;
}

await sendRpc('tools/call', retryParams);
logger.debug('test_mrtr_echo_state: MRTR flow completed');
}

// Tool 2: test_mrtr_no_state — call, get InputRequiredResult WITHOUT requestState, retry without it
const r2 = await sendRpc('tools/call', {
name: 'test_mrtr_no_state',
arguments: {}
});

const r2Result = r2.result as Record<string, unknown> | undefined;
if (r2Result?.resultType === 'input_required') {
const inputRequests = r2Result.inputRequests as Record<string, unknown>;

// Build inputResponses
const inputResponses: Record<string, unknown> = {};
for (const [key, req] of Object.entries(inputRequests)) {
const request = req as { method: string; params: unknown };
if (request.method === 'elicitation/create') {
inputResponses[key] = {
action: 'accept',
content: { confirmed: true }
};
}
}

// Retry WITHOUT requestState (server didn't send one)
await sendRpc('tools/call', {
name: 'test_mrtr_no_state',
arguments: {},
inputResponses
});
logger.debug('test_mrtr_no_state: MRTR flow completed');
}

// Tool 3: test_mrtr_no_result_type — returns result without resultType field
// Client must treat it as complete (default) and NOT retry
const r3 = await sendRpc('tools/call', {
name: 'test_mrtr_no_result_type',
arguments: {}
});

const r3Result = r3.result as Record<string, unknown> | undefined;
if (r3Result && !r3Result.resultType) {
// No resultType means default to "complete" — do nothing, don't retry
logger.debug(
'test_mrtr_no_result_type: result has no resultType, treating as complete'
);
}

logger.debug('MRTR client scenario completed');
}

registerScenario('sep-2322-client-request-state', runMRTRClient);

// ============================================================================
// Main entry point
// ============================================================================
Expand Down
Loading
Loading