Skip to content

Commit 8ce4abe

Browse files
aayush-kapoorvercel-ai-sdk[bot]
authored andcommitted
feat(mcp): surface 'serverInfo' exposed from the MCP server (#14288)
## Background introduced in #13825 we used to capture the `serverInfo` in the schema but never surface it through to the client. only `result.capabilities` was stored and `result.serverInfo` was thrown away. there was no field on the client to hold it and no property on the MCPClient interface to access it ## Summary - added `title` field to `ClientOrServerImplementationSchema` to align with [MCP spec](https://modelcontextprotocol.io/specification/2025-11-25/schema#implementation) - exposed `serverInfo` via a getter function ## Manual Verification verified by running the client and server under `examples/mcp/src/server-info` ## Checklist - [x] Tests have been added / updated (for bug fixes / features) - [ ] Documentation has been added / updated (for bug fixes / features) - [x] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) - [x] I have reviewed this pull request (self-review)
1 parent 1eea534 commit 8ce4abe

File tree

7 files changed

+110
-0
lines changed

7 files changed

+110
-0
lines changed

.changeset/gentle-snakes-flash.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@ai-sdk/mcp": patch
3+
---
4+
5+
feat(mcp): surface 'serverInfo' exposed from the MCP server
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { createMCPClient } from '@ai-sdk/mcp';
2+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
3+
4+
async function main() {
5+
const client = await createMCPClient({
6+
transport: new StreamableHTTPClientTransport(
7+
new URL('http://localhost:3000/mcp'),
8+
),
9+
});
10+
11+
console.log('serverInfo:', client.serverInfo);
12+
13+
await client.close();
14+
}
15+
16+
main().catch(console.error);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
3+
import express from 'express';
4+
5+
const app = express();
6+
app.use(express.json());
7+
8+
app.post('/mcp', async (req, res) => {
9+
const server = new McpServer({
10+
name: 'my-dumb-server',
11+
version: '2.000.0',
12+
});
13+
14+
server.tool('ping', 'A simple ping tool', async () => {
15+
return { content: [{ type: 'text', text: 'pong' }] };
16+
});
17+
18+
const transport = new StreamableHTTPServerTransport({
19+
sessionIdGenerator: undefined,
20+
});
21+
await server.connect(transport);
22+
await transport.handleRequest(req, res, req.body);
23+
res.on('close', () => {
24+
transport.close();
25+
server.close();
26+
});
27+
});
28+
29+
app.listen(3000, () => {
30+
console.log('server-info example server listening on port 3000');
31+
});

packages/mcp/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export {
1414
} from './tool/mcp-client';
1515
export { ElicitationRequestSchema, ElicitResultSchema } from './tool/types';
1616
export type {
17+
Configuration,
1718
ElicitationRequest,
1819
ElicitResult,
1920
ListToolsResult,

packages/mcp/src/tool/mcp-client.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -914,6 +914,48 @@ describe('MCPClient', () => {
914914
}
915915
});
916916

917+
it('should expose serverInfo from initialization', async () => {
918+
createMockTransport.mockImplementation(
919+
() =>
920+
new MockMCPTransport({
921+
initializeResult: {
922+
protocolVersion: '2025-11-25',
923+
serverInfo: {
924+
name: 'my-server',
925+
version: '2.0.0',
926+
title: 'My Server',
927+
},
928+
capabilities: { tools: {} },
929+
},
930+
}),
931+
);
932+
933+
client = await createMCPClient({
934+
transport: { type: 'sse', url: 'https://example.com/sse' },
935+
});
936+
937+
expect(client.serverInfo).toMatchInlineSnapshot(`
938+
{
939+
"name": "my-server",
940+
"title": "My Server",
941+
"version": "2.0.0",
942+
}
943+
`);
944+
});
945+
946+
it('should expose serverInfo without title when server omits it', async () => {
947+
client = await createMCPClient({
948+
transport: { type: 'sse', url: 'https://example.com/sse' },
949+
});
950+
951+
expect(client.serverInfo).toMatchInlineSnapshot(`
952+
{
953+
"name": "mock-mcp-server",
954+
"version": "1.0.0",
955+
}
956+
`);
957+
});
958+
917959
it('should close transport when client is closed', async () => {
918960
const mockTransport = new MockMCPTransport();
919961
const closeSpy = vi.spyOn(mockTransport, 'close');

packages/mcp/src/tool/mcp-client.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
CallToolResult,
3030
CallToolResultSchema,
3131
ClientCapabilities,
32+
Configuration,
3233
Configuration as ClientConfiguration,
3334
ElicitationRequest,
3435
ElicitationRequestSchema,
@@ -119,6 +120,12 @@ export async function createMCPClient(
119120
}
120121

121122
export interface MCPClient {
123+
/**
124+
* Information about the connected MCP server, as reported during initialization.
125+
* @see https://modelcontextprotocol.io/specification/2025-11-25/schema#implementation
126+
*/
127+
readonly serverInfo: Configuration;
128+
122129
tools<TOOL_SCHEMAS extends ToolSchemas = 'automatic'>(options?: {
123130
schemas?: TOOL_SCHEMAS;
124131
}): Promise<McpToolSet<TOOL_SCHEMAS>>;
@@ -201,6 +208,7 @@ class DefaultMCPClient implements MCPClient {
201208
(response: JSONRPCResponse | Error) => void
202209
> = new Map();
203210
private serverCapabilities: ServerCapabilities = {};
211+
private _serverInfo: Configuration = { name: '', version: '' };
204212
private isClosed = true;
205213
private elicitationRequestHandler?: (
206214
request: ElicitationRequest,
@@ -247,6 +255,10 @@ class DefaultMCPClient implements MCPClient {
247255
};
248256
}
249257

258+
get serverInfo(): Configuration {
259+
return this._serverInfo;
260+
}
261+
250262
async init(): Promise<this> {
251263
try {
252264
await this.transport.start();
@@ -277,6 +289,7 @@ class DefaultMCPClient implements MCPClient {
277289
}
278290

279291
this.serverCapabilities = result.capabilities;
292+
this._serverInfo = result.serverInfo;
280293

281294
// Complete initialization handshake:
282295
await this.notification({

packages/mcp/src/tool/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,10 @@ export type McpToolSet<TOOL_SCHEMAS extends ToolSchemas = 'automatic'> =
5656
const ClientOrServerImplementationSchema = z.looseObject({
5757
name: z.string(),
5858
version: z.string(),
59+
title: z.optional(z.string()),
5960
});
6061

62+
// Maps to `Implementation` in the MCP specification
6163
export type Configuration = z.infer<typeof ClientOrServerImplementationSchema>;
6264

6365
export const BaseParamsSchema = z.looseObject({

0 commit comments

Comments
 (0)