Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions mcp-client-typescript/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# An LLM-Powered Chatbot MCP Client written in TypeScript

See the [Build an MCP client](https://modelcontextprotocol.io/docs/develop/build-client) tutorial for more information.

## Structured output

The SDK validates every result against the tool's declared `outputSchema`, so the spec's client-side SHOULD needs no code here.

The two channels go to different readers: `content` is forwarded to the model, while `structuredContent` is used as data — the client counts the items it returns. See [Structured Content](https://modelcontextprotocol.io/specification/draft/server/tools#structured-content).

`versionNegotiation: { mode: 'auto' }` probes `server/discover` and falls back to the `2025-11-25` handshake; the SDK's default is `'legacy'`. See the [SDK documentation](https://ts.sdk.modelcontextprotocol.io/v2/).
25 changes: 20 additions & 5 deletions mcp-client-typescript/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import {
ToolUseBlock,
} from "@anthropic-ai/sdk/resources/messages/messages.mjs";

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { Client } from "@modelcontextprotocol/client";
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
import readline from "readline/promises";

import dotenv from "dotenv";

dotenv.config(); // load environment variables from .env

const ANTHROPIC_MODEL = "claude-sonnet-4-5";
const ANTHROPIC_MODEL = "claude-sonnet-5";
const MAX_TOOL_TURNS = 10;

class MCPClient {
Expand All @@ -25,8 +25,12 @@ class MCPClient {
private tools: Tool[] = [];

constructor() {
// Initialize MCP client
this.mcp = new Client({ name: "mcp-client-cli", version: "1.0.0" });
// 'auto' probes server/discover, falling back to the 2025-11-25 handshake.
// The SDK's default is 'legacy'.
this.mcp = new Client(
{ name: "mcp-client-cli", version: "1.0.0" },
{ versionNegotiation: { mode: "auto" } },
);
}

private get anthropic(): Anthropic {
Expand Down Expand Up @@ -123,14 +127,25 @@ class MCPClient {
finalText.push(
`[Calling tool ${toolUse.name} with args ${JSON.stringify(toolArgs)}]`,
);
// callTool validates the result against the declared schema.
const result = await this.mcp.callTool({
name: toolUse.name,
arguments: toolArgs,
});

// structuredContent is data the application can use directly.
if (Array.isArray(result.structuredContent)) {
finalText.push(
`[${toolUse.name} returned ${result.structuredContent.length} items]`,
);
}

// content is what the model reads.
toolResults.push({
type: "tool_result",
tool_use_id: toolUse.id,
content: result.content as ToolResultBlockParam["content"],
is_error: result.isError === true,
});
}

Expand Down
Loading
Loading