Skip to content

Commit 54c3010

Browse files
committed
Merge branch 'main' into ihrpr/elicitation
2 parents c088719 + 61ef3c9 commit 54c3010

20 files changed

+2212
-271
lines changed

README.md

Lines changed: 239 additions & 24 deletions
Large diffs are not rendered by default.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@modelcontextprotocol/sdk",
3-
"version": "1.12.2",
3+
"version": "1.12.3",
44
"description": "Model Context Protocol implementation for TypeScript",
55
"license": "MIT",
66
"author": "Anthropic, PBC (https://anthropic.com)",

src/client/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ export class Client<
165165

166166
this._serverCapabilities = result.capabilities;
167167
this._serverVersion = result.serverInfo;
168+
// HTTP transports must set the protocol version in each header after initialization.
169+
if (transport.setProtocolVersion) {
170+
transport.setProtocolVersion(result.protocolVersion);
171+
}
168172

169173
this._instructions = result.instructions;
170174

src/client/sse.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export class SSEClientTransport implements Transport {
6262
private _eventSourceInit?: EventSourceInit;
6363
private _requestInit?: RequestInit;
6464
private _authProvider?: OAuthClientProvider;
65+
private _protocolVersion?: string;
6566

6667
onclose?: () => void;
6768
onerror?: (error: Error) => void;
@@ -99,13 +100,18 @@ export class SSEClientTransport implements Transport {
99100
}
100101

101102
private async _commonHeaders(): Promise<HeadersInit> {
102-
const headers: HeadersInit = { ...this._requestInit?.headers };
103+
const headers = {
104+
...this._requestInit?.headers,
105+
} as HeadersInit & Record<string, string>;
103106
if (this._authProvider) {
104107
const tokens = await this._authProvider.tokens();
105108
if (tokens) {
106-
(headers as Record<string, string>)["Authorization"] = `Bearer ${tokens.access_token}`;
109+
headers["Authorization"] = `Bearer ${tokens.access_token}`;
107110
}
108111
}
112+
if (this._protocolVersion) {
113+
headers["mcp-protocol-version"] = this._protocolVersion;
114+
}
109115

110116
return headers;
111117
}
@@ -214,7 +220,7 @@ export class SSEClientTransport implements Transport {
214220

215221
try {
216222
const commonHeaders = await this._commonHeaders();
217-
const headers = new Headers({ ...commonHeaders, ...this._requestInit?.headers });
223+
const headers = new Headers(commonHeaders);
218224
headers.set("content-type", "application/json");
219225
const init = {
220226
...this._requestInit,
@@ -249,4 +255,8 @@ export class SSEClientTransport implements Transport {
249255
throw error;
250256
}
251257
}
258+
259+
setProtocolVersion(version: string): void {
260+
this._protocolVersion = version;
261+
}
252262
}

src/client/streamableHttp.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ export class StreamableHTTPClientTransport implements Transport {
124124
private _authProvider?: OAuthClientProvider;
125125
private _sessionId?: string;
126126
private _reconnectionOptions: StreamableHTTPReconnectionOptions;
127+
private _protocolVersion?: string;
127128

128129
onclose?: () => void;
129130
onerror?: (error: Error) => void;
@@ -162,7 +163,7 @@ export class StreamableHTTPClientTransport implements Transport {
162163
}
163164

164165
private async _commonHeaders(): Promise<Headers> {
165-
const headers: HeadersInit = {};
166+
const headers: HeadersInit & Record<string, string> = {};
166167
if (this._authProvider) {
167168
const tokens = await this._authProvider.tokens();
168169
if (tokens) {
@@ -173,6 +174,9 @@ export class StreamableHTTPClientTransport implements Transport {
173174
if (this._sessionId) {
174175
headers["mcp-session-id"] = this._sessionId;
175176
}
177+
if (this._protocolVersion) {
178+
headers["mcp-protocol-version"] = this._protocolVersion;
179+
}
176180

177181
return new Headers(
178182
{ ...headers, ...this._requestInit?.headers }
@@ -516,4 +520,11 @@ export class StreamableHTTPClientTransport implements Transport {
516520
throw error;
517521
}
518522
}
523+
524+
setProtocolVersion(version: string): void {
525+
this._protocolVersion = version;
526+
}
527+
get protocolVersion(): string | undefined {
528+
return this._protocolVersion;
529+
}
519530
}

src/examples/client/simpleStreamableHttp.ts

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ import {
1515
LoggingMessageNotificationSchema,
1616
ResourceListChangedNotificationSchema,
1717
ElicitRequestSchema,
18+
ResourceLink,
19+
ReadResourceRequest,
20+
ReadResourceResultSchema,
1821
} from '../../types.js';
22+
import { getDisplayName } from '../../shared/metadataUtils.js';
1923
import Ajv from "ajv";
2024

2125
// Create readline interface for user input
@@ -62,6 +66,7 @@ function printHelp(): void {
6266
console.log(' list-prompts - List available prompts');
6367
console.log(' get-prompt [name] [args] - Get a prompt with optional JSON arguments');
6468
console.log(' list-resources - List available resources');
69+
console.log(' read-resource <uri> - Read a specific resource by URI');
6570
console.log(' help - Show this help');
6671
console.log(' quit - Exit the program');
6772
}
@@ -161,6 +166,14 @@ function commandLoop(): void {
161166
await listResources();
162167
break;
163168

169+
case 'read-resource':
170+
if (args.length < 2) {
171+
console.log('Usage: read-resource <uri>');
172+
} else {
173+
await readResource(args[1]);
174+
}
175+
break;
176+
164177
case 'help':
165178
printHelp();
166179
break;
@@ -521,7 +534,7 @@ async function listTools(): Promise<void> {
521534
console.log(' No tools available');
522535
} else {
523536
for (const tool of toolsResult.tools) {
524-
console.log(` - ${tool.name}: ${tool.description}`);
537+
console.log(` - id: ${tool.name}, name: ${getDisplayName(tool)}, description: ${tool.description}`);
525538
}
526539
}
527540
} catch (error) {
@@ -548,13 +561,37 @@ async function callTool(name: string, args: Record<string, unknown>): Promise<vo
548561
const result = await client.request(request, CallToolResultSchema);
549562

550563
console.log('Tool result:');
564+
const resourceLinks: ResourceLink[] = [];
565+
551566
result.content.forEach(item => {
552567
if (item.type === 'text') {
553568
console.log(` ${item.text}`);
569+
} else if (item.type === 'resource_link') {
570+
const resourceLink = item as ResourceLink;
571+
resourceLinks.push(resourceLink);
572+
console.log(` 📁 Resource Link: ${resourceLink.name}`);
573+
console.log(` URI: ${resourceLink.uri}`);
574+
if (resourceLink.mimeType) {
575+
console.log(` Type: ${resourceLink.mimeType}`);
576+
}
577+
if (resourceLink.description) {
578+
console.log(` Description: ${resourceLink.description}`);
579+
}
580+
} else if (item.type === 'resource') {
581+
console.log(` [Embedded Resource: ${item.resource.uri}]`);
582+
} else if (item.type === 'image') {
583+
console.log(` [Image: ${item.mimeType}]`);
584+
} else if (item.type === 'audio') {
585+
console.log(` [Audio: ${item.mimeType}]`);
554586
} else {
555-
console.log(` ${item.type} content:`, item);
587+
console.log(` [Unknown content type]:`, item);
556588
}
557589
});
590+
591+
// Offer to read resource links
592+
if (resourceLinks.length > 0) {
593+
console.log(`\nFound ${resourceLinks.length} resource link(s). Use 'read-resource <uri>' to read their content.`);
594+
}
558595
} catch (error) {
559596
console.log(`Error calling tool ${name}: ${error}`);
560597
}
@@ -589,7 +626,7 @@ async function runNotificationsToolWithResumability(interval: number, count: num
589626
try {
590627
console.log(`Starting notification stream with resumability: interval=${interval}ms, count=${count || 'unlimited'}`);
591628
console.log(`Using resumption token: ${notificationsToolLastEventId || 'none'}`);
592-
629+
593630
const request: CallToolRequest = {
594631
method: 'tools/call',
595632
params: {
@@ -602,7 +639,7 @@ async function runNotificationsToolWithResumability(interval: number, count: num
602639
notificationsToolLastEventId = event;
603640
console.log(`Updated resumption token: ${event}`);
604641
};
605-
642+
606643
const result = await client.request(request, CallToolResultSchema, {
607644
resumptionToken: notificationsToolLastEventId,
608645
onresumptiontoken: onLastEventIdUpdate
@@ -638,7 +675,7 @@ async function listPrompts(): Promise<void> {
638675
console.log(' No prompts available');
639676
} else {
640677
for (const prompt of promptsResult.prompts) {
641-
console.log(` - ${prompt.name}: ${prompt.description}`);
678+
console.log(` - id: ${prompt.name}, name: ${getDisplayName(prompt)}, description: ${prompt.description}`);
642679
}
643680
}
644681
} catch (error) {
@@ -689,14 +726,50 @@ async function listResources(): Promise<void> {
689726
console.log(' No resources available');
690727
} else {
691728
for (const resource of resourcesResult.resources) {
692-
console.log(` - ${resource.name}: ${resource.uri}`);
729+
console.log(` - id: ${resource.name}, name: ${getDisplayName(resource)}, description: ${resource.uri}`);
693730
}
694731
}
695732
} catch (error) {
696733
console.log(`Resources not supported by this server (${error})`);
697734
}
698735
}
699736

737+
async function readResource(uri: string): Promise<void> {
738+
if (!client) {
739+
console.log('Not connected to server.');
740+
return;
741+
}
742+
743+
try {
744+
const request: ReadResourceRequest = {
745+
method: 'resources/read',
746+
params: { uri }
747+
};
748+
749+
console.log(`Reading resource: ${uri}`);
750+
const result = await client.request(request, ReadResourceResultSchema);
751+
752+
console.log('Resource contents:');
753+
for (const content of result.contents) {
754+
console.log(` URI: ${content.uri}`);
755+
if (content.mimeType) {
756+
console.log(` Type: ${content.mimeType}`);
757+
}
758+
759+
if ('text' in content && typeof content.text === 'string') {
760+
console.log(' Content:');
761+
console.log(' ---');
762+
console.log(content.text.split('\n').map((line: string) => ' ' + line).join('\n'));
763+
console.log(' ---');
764+
} else if ('blob' in content && typeof content.blob === 'string') {
765+
console.log(` [Binary data: ${content.blob.length} bytes]`);
766+
}
767+
}
768+
} catch (error) {
769+
console.log(`Error reading resource ${uri}: ${error}`);
770+
}
771+
}
772+
700773
async function cleanup(): Promise<void> {
701774
if (client && transport) {
702775
try {

0 commit comments

Comments
 (0)