Compare commits

..

1 Commits

Author SHA1 Message Date
Ashwin Bhat
8cd1dc7dd4 fix resource reading type error 2024-10-15 14:58:48 -07:00
108 changed files with 20037 additions and 6308 deletions

3
.gitattributes vendored
View File

@@ -1 +1,2 @@
package-lock.json linguist-generated=true yarn.lock linguist-generated=true
packages/**/* linguist-generated=true

View File

@@ -1,37 +0,0 @@
on:
push:
branches:
- main
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
cache: npm
- uses: actions/checkout@v4
with:
repository: modelcontextprotocol/typescript-sdk
ssh-key: ${{ secrets.TYPESCRIPT_SDK_KEY }}
path: packages/@modelcontextprotocol/sdk
- run: npm ci
working-directory: packages/@modelcontextprotocol/sdk
- run: npm pack
working-directory: packages/@modelcontextprotocol/sdk
- run: npm install --save packages/@modelcontextprotocol/sdk/modelcontextprotocol-sdk-*.tgz
# Working around https://github.com/npm/cli/issues/4828
# - run: npm ci
- run: npm install --no-package-lock
- run: npm run build

1
.gitignore vendored
View File

@@ -3,4 +3,3 @@ node_modules
server/build server/build
client/dist client/dist
client/tsconfig.app.tsbuildinfo client/tsconfig.app.tsbuildinfo
client/tsconfig.node.tsbuildinfo

1
.npmrc
View File

@@ -1 +0,0 @@
registry = "https://registry.npmjs.org/"

View File

@@ -2,23 +2,16 @@
The MCP inspector is a developer tool for testing and debugging MCP servers. The MCP inspector is a developer tool for testing and debugging MCP servers.
## Getting started Setup:
This repository depends on the [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk/). Until these repositories are made public and published to npm, the SDK has to be preinstalled manually: ```bash
yarn
1. Download the [latest release of the SDK](https://github.com/modelcontextprotocol/typescript-sdk/releases) (the file named something like `modelcontextprotocol-sdk-0.1.0.tgz`). You don't need to extract it.
2. From within your checkout of _this_ repository, run `npm install --save path/to/sdk.tgz`. This will overwrite the expected location for the SDK to allow you to proceed.
Then, you should be able to install the rest of the dependencies normally:
```sh
npm install
``` ```
You can run it in dev mode via: You can run it in dev mode via:
```bash ```bash
npm run dev yarn dev
``` ```
This will start both the client and server. This will start both the client and server.
@@ -26,6 +19,6 @@ This will start both the client and server.
To run in production mode: To run in production mode:
```bash ```bash
npm run build yarn build
npm start yarn start
``` ```

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MCP Inspector</title> <title>Vite + React + TS</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

@@ -10,7 +10,6 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@modelcontextprotocol/sdk": "*",
"@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.1.0", "@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-select": "^2.1.2", "@radix-ui/react-select": "^2.1.2",
@@ -19,11 +18,11 @@
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"lucide-react": "^0.447.0", "lucide-react": "^0.447.0",
"mcp-typescript": "file:../packages/mcp-typescript",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"tailwind-merge": "^2.5.3", "tailwind-merge": "^2.5.3",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7"
"zod": "^3.23.8"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.11.1", "@eslint/js": "^9.11.1",

View File

@@ -1,5 +1,8 @@
#root { #root {
max-width: 1280px;
margin: 0 auto; margin: 0 auto;
padding: 2rem;
text-align: center;
} }
.logo { .logo {

View File

@@ -1,25 +1,29 @@
import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { Client } from "mcp-typescript/client/index.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"; import { SSEClientTransport } from "mcp-typescript/client/sse.js";
import { import {
CallToolResultSchema,
ClientRequest,
CreateMessageRequestSchema,
CreateMessageResult,
EmptyResultSchema,
GetPromptResultSchema,
ListPromptsResultSchema,
ListResourcesResultSchema, ListResourcesResultSchema,
GetPromptResultSchema,
ListToolsResultSchema, ListToolsResultSchema,
ProgressNotificationSchema,
ReadResourceResultSchema, ReadResourceResultSchema,
CallToolResultSchema,
ListPromptsResultSchema,
Resource, Resource,
ServerNotification,
Tool, Tool,
} from "@modelcontextprotocol/sdk/types.js"; ClientRequest,
import { useEffect, useRef, useState } from "react"; } from "mcp-typescript/types.js";
import { useState } from "react";
import { Button } from "@/components/ui/button"; import {
Send,
Bell,
Terminal,
Files,
MessageSquare,
Hammer,
Play,
} from "lucide-react";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { import {
Select, Select,
SelectContent, SelectContent,
@@ -27,29 +31,16 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import {
Bell,
Files,
Hammer,
Hash,
MessageSquare,
Play,
Send,
Terminal,
} from "lucide-react";
import { AnyZodObject } from "zod";
import "./App.css";
import ConsoleTab from "./components/ConsoleTab"; import ConsoleTab from "./components/ConsoleTab";
import HistoryAndNotifications from "./components/History"; import Sidebar from "./components/Sidebar";
import PingTab from "./components/PingTab";
import PromptsTab, { Prompt } from "./components/PromptsTab";
import RequestsTab from "./components/RequestsTabs"; import RequestsTab from "./components/RequestsTabs";
import ResourcesTab from "./components/ResourcesTab"; import ResourcesTab from "./components/ResourcesTab";
import SamplingTab, { PendingRequest } from "./components/SamplingTab"; import NotificationsTab from "./components/NotificationsTab";
import Sidebar from "./components/Sidebar"; import PromptsTab, { Prompt } from "./components/PromptsTab";
import ToolsTab from "./components/ToolsTab"; import ToolsTab from "./components/ToolsTab";
import History from "./components/History";
import { AnyZodObject } from "node_modules/zod/lib";
const App = () => { const App = () => {
const [connectionStatus, setConnectionStatus] = useState< const [connectionStatus, setConnectionStatus] = useState<
@@ -62,70 +53,24 @@ const App = () => {
const [tools, setTools] = useState<Tool[]>([]); const [tools, setTools] = useState<Tool[]>([]);
const [toolResult, setToolResult] = useState<string>(""); const [toolResult, setToolResult] = useState<string>("");
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [command, setCommand] = useState<string>(() => { const [command, setCommand] = useState<string>(
return ( "/Users/ashwin/.nvm/versions/node/v18.20.4/bin/node",
localStorage.getItem("lastCommand") || );
"/Users/ashwin/.nvm/versions/node/v18.20.4/bin/node" const [args, setArgs] = useState<string>(
); "/Users/ashwin/code/example-servers/build/everything/stdio.js",
}); );
const [args, setArgs] = useState<string>(() => {
return localStorage.getItem("lastArgs") || "";
});
const [url, setUrl] = useState<string>("http://localhost:3001/sse"); const [url, setUrl] = useState<string>("http://localhost:3001/sse");
const [transportType, setTransportType] = useState<"stdio" | "sse">("stdio"); const [transportType, setTransportType] = useState<"stdio" | "sse">("stdio");
const [requestHistory, setRequestHistory] = useState< const [requestHistory, setRequestHistory] = useState<
{ request: string; response: string }[] { request: string; response: string }[]
>([]); >([]);
const [mcpClient, setMcpClient] = useState<Client | null>(null); const [mcpClient, setMcpClient] = useState<Client | null>(null);
const [notifications, setNotifications] = useState<ServerNotification[]>([]);
const [pendingSampleRequests, setPendingSampleRequests] = useState<
Array<
PendingRequest & {
resolve: (result: CreateMessageResult) => void;
reject: (error: Error) => void;
}
>
>([]);
const nextRequestId = useRef(0);
const handleApproveSampling = (id: number, result: CreateMessageResult) => {
setPendingSampleRequests((prev) => {
const request = prev.find((r) => r.id === id);
request?.resolve(result);
return prev.filter((r) => r.id !== id);
});
};
const handleRejectSampling = (id: number) => {
setPendingSampleRequests((prev) => {
const request = prev.find((r) => r.id === id);
request?.reject(new Error("Sampling request rejected"));
return prev.filter((r) => r.id !== id);
});
};
const [selectedResource, setSelectedResource] = useState<Resource | null>( const [selectedResource, setSelectedResource] = useState<Resource | null>(
null, null,
); );
const [selectedPrompt, setSelectedPrompt] = useState<Prompt | null>(null); const [selectedPrompt, setSelectedPrompt] = useState<Prompt | null>(null);
const [selectedTool, setSelectedTool] = useState<Tool | null>(null); const [selectedTool, setSelectedTool] = useState<Tool | null>(null);
const [nextResourceCursor, setNextResourceCursor] = useState<
string | undefined
>();
const [nextPromptCursor, setNextPromptCursor] = useState<
string | undefined
>();
const [nextToolCursor, setNextToolCursor] = useState<string | undefined>();
const progressTokenRef = useRef(0);
useEffect(() => {
localStorage.setItem("lastCommand", command);
}, [command]);
useEffect(() => {
localStorage.setItem("lastArgs", args);
}, [args]);
const pushHistory = (request: object, response: object) => { const pushHistory = (request: object, response: object) => {
setRequestHistory((prev) => [ setRequestHistory((prev) => [
@@ -156,15 +101,15 @@ const App = () => {
const response = await makeRequest( const response = await makeRequest(
{ {
method: "resources/list" as const, method: "resources/list" as const,
params: nextResourceCursor ? { cursor: nextResourceCursor } : {},
}, },
ListResourcesResultSchema, ListResourcesResultSchema,
); );
setResources(resources.concat(response.resources ?? [])); if (response.resources) {
setNextResourceCursor(response.nextCursor); setResources(response.resources);
}
}; };
const readResource = async (uri: string) => { const readResource = async (uri: URL) => {
const response = await makeRequest( const response = await makeRequest(
{ {
method: "resources/read" as const, method: "resources/read" as const,
@@ -179,12 +124,10 @@ const App = () => {
const response = await makeRequest( const response = await makeRequest(
{ {
method: "prompts/list" as const, method: "prompts/list" as const,
params: nextPromptCursor ? { cursor: nextPromptCursor } : {},
}, },
ListPromptsResultSchema, ListPromptsResultSchema,
); );
setPrompts(response.prompts); setPrompts(response.prompts);
setNextPromptCursor(response.nextCursor);
}; };
const getPrompt = async (name: string, args: Record<string, string> = {}) => { const getPrompt = async (name: string, args: Record<string, string> = {}) => {
@@ -202,25 +145,17 @@ const App = () => {
const response = await makeRequest( const response = await makeRequest(
{ {
method: "tools/list" as const, method: "tools/list" as const,
params: nextToolCursor ? { cursor: nextToolCursor } : {},
}, },
ListToolsResultSchema, ListToolsResultSchema,
); );
setTools(response.tools); setTools(response.tools);
setNextToolCursor(response.nextCursor);
}; };
const callTool = async (name: string, params: Record<string, unknown>) => { const callTool = async (name: string, params: Record<string, unknown>) => {
const response = await makeRequest( const response = await makeRequest(
{ {
method: "tools/call" as const, method: "tools/call" as const,
params: { params: { name, arguments: params },
name,
arguments: params,
_meta: {
progressToken: progressTokenRef.current++,
},
},
}, },
CallToolResultSchema, CallToolResultSchema,
); );
@@ -234,6 +169,7 @@ const App = () => {
version: "0.0.1", version: "0.0.1",
}); });
const clientTransport = new SSEClientTransport();
const backendUrl = new URL("http://localhost:3000/sse"); const backendUrl = new URL("http://localhost:3000/sse");
backendUrl.searchParams.append("transportType", transportType); backendUrl.searchParams.append("transportType", transportType);
@@ -244,28 +180,9 @@ const App = () => {
backendUrl.searchParams.append("url", url); backendUrl.searchParams.append("url", url);
} }
const clientTransport = new SSEClientTransport(backendUrl); await clientTransport.connect(backendUrl);
await client.connect(clientTransport); await client.connect(clientTransport);
client.setNotificationHandler(
ProgressNotificationSchema,
(notification) => {
setNotifications((prevNotifications) => [
...prevNotifications,
notification,
]);
},
);
client.setRequestHandler(CreateMessageRequestSchema, (request) => {
return new Promise<CreateMessageResult>((resolve, reject) => {
setPendingSampleRequests((prev) => [
...prev,
{ id: nextRequestId.current++, request, resolve, reject },
]);
});
});
setMcpClient(client); setMcpClient(client);
setConnectionStatus("connected"); setConnectionStatus("connected");
} catch (e) { } catch (e) {
@@ -339,6 +256,10 @@ const App = () => {
<Send className="w-4 h-4 mr-2" /> <Send className="w-4 h-4 mr-2" />
Requests Requests
</TabsTrigger> </TabsTrigger>
<TabsTrigger value="notifications" disabled>
<Bell className="w-4 h-4 mr-2" />
Notifications
</TabsTrigger>
<TabsTrigger value="tools"> <TabsTrigger value="tools">
<Hammer className="w-4 h-4 mr-2" /> <Hammer className="w-4 h-4 mr-2" />
Tools Tools
@@ -347,19 +268,6 @@ const App = () => {
<Terminal className="w-4 h-4 mr-2" /> <Terminal className="w-4 h-4 mr-2" />
Console Console
</TabsTrigger> </TabsTrigger>
<TabsTrigger value="ping">
<Bell className="w-4 h-4 mr-2" />
Ping
</TabsTrigger>
<TabsTrigger value="sampling" className="relative">
<Hash className="w-4 h-4 mr-2" />
Sampling
{pendingSampleRequests.length > 0 && (
<span className="absolute -top-1 -right-1 bg-red-500 text-white text-xs rounded-full h-4 w-4 flex items-center justify-center">
{pendingSampleRequests.length}
</span>
)}
</TabsTrigger>
</TabsList> </TabsList>
<div className="w-full"> <div className="w-full">
@@ -370,9 +278,9 @@ const App = () => {
selectedResource={selectedResource} selectedResource={selectedResource}
setSelectedResource={setSelectedResource} setSelectedResource={setSelectedResource}
resourceContent={resourceContent} resourceContent={resourceContent}
nextCursor={nextResourceCursor}
error={error} error={error}
/> />
<NotificationsTab />
<PromptsTab <PromptsTab
prompts={prompts} prompts={prompts}
listPrompts={listPrompts} listPrompts={listPrompts}
@@ -380,7 +288,6 @@ const App = () => {
selectedPrompt={selectedPrompt} selectedPrompt={selectedPrompt}
setSelectedPrompt={setSelectedPrompt} setSelectedPrompt={setSelectedPrompt}
promptContent={promptContent} promptContent={promptContent}
nextCursor={nextPromptCursor}
error={error} error={error}
/> />
<RequestsTab /> <RequestsTab />
@@ -394,25 +301,9 @@ const App = () => {
setToolResult(""); setToolResult("");
}} }}
toolResult={toolResult} toolResult={toolResult}
nextCursor={nextToolCursor}
error={error} error={error}
/> />
<ConsoleTab /> <ConsoleTab />
<PingTab
onPingClick={() => {
void makeRequest(
{
method: "ping" as const,
},
EmptyResultSchema,
);
}}
/>
<SamplingTab
pendingRequests={pendingSampleRequests}
onApprove={handleApproveSampling}
onReject={handleRejectSampling}
/>
</div> </div>
</Tabs> </Tabs>
) : ( ) : (
@@ -425,10 +316,7 @@ const App = () => {
</div> </div>
</div> </div>
</div> </div>
<HistoryAndNotifications <History requestHistory={requestHistory} />
requestHistory={requestHistory}
serverNotifications={notifications}
/>
</div> </div>
); );
}; };

View File

@@ -1,163 +1,94 @@
import { ServerNotification } from "@modelcontextprotocol/sdk/types.js";
import { Copy } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { Copy } from "lucide-react";
const HistoryAndNotifications = ({ const History = ({
requestHistory, requestHistory,
serverNotifications,
}: { }: {
requestHistory: Array<{ request: string; response: string | null }>; requestHistory: Array<{ request: string; response: string | null }>;
serverNotifications: ServerNotification[];
}) => { }) => {
const [expandedRequests, setExpandedRequests] = useState<{ const [expandedRequests, setExpandedRequests] = useState<{
[key: number]: boolean; [key: number]: boolean;
}>({}); }>({});
const [expandedNotifications, setExpandedNotifications] = useState<{
[key: number]: boolean;
}>({});
const toggleRequestExpansion = (index: number) => { const toggleRequestExpansion = (index: number) => {
setExpandedRequests((prev) => ({ ...prev, [index]: !prev[index] })); setExpandedRequests((prev) => ({ ...prev, [index]: !prev[index] }));
}; };
const toggleNotificationExpansion = (index: number) => {
setExpandedNotifications((prev) => ({ ...prev, [index]: !prev[index] }));
};
const copyToClipboard = (text: string) => { const copyToClipboard = (text: string) => {
navigator.clipboard.writeText(text); navigator.clipboard.writeText(text);
}; };
return ( return (
<div className="w-64 bg-white shadow-md p-4 overflow-hidden flex flex-col h-full"> <div className="w-64 bg-white shadow-md p-4 overflow-y-auto">
<div className="flex-1 overflow-y-auto mb-4 border-b pb-4"> <h2 className="text-lg font-semibold mb-4">History</h2>
<h2 className="text-lg font-semibold mb-4">History</h2> <ul className="space-y-3">
{requestHistory.length === 0 ? ( {requestHistory
<p className="text-sm text-gray-500 italic">No history yet</p> .slice()
) : ( .reverse()
<ul className="space-y-3"> .map((request, index) => (
{requestHistory <li
.slice() key={index}
.reverse() className="text-sm text-gray-600 bg-gray-100 p-2 rounded"
.map((request, index) => ( >
<li <div
key={index} className="flex justify-between items-center cursor-pointer"
className="text-sm text-gray-600 bg-gray-100 p-2 rounded" onClick={() =>
> toggleRequestExpansion(requestHistory.length - 1 - index)
<div }
className="flex justify-between items-center cursor-pointer" >
onClick={() => <span className="font-mono">
toggleRequestExpansion(requestHistory.length - 1 - index) {requestHistory.length - index}.{" "}
} {JSON.parse(request.request).method}
> </span>
<span className="font-mono"> <span>
{requestHistory.length - index}.{" "} {expandedRequests[requestHistory.length - 1 - index]
{JSON.parse(request.request).method} ? "▼"
</span> : "▶"}
<span> </span>
{expandedRequests[requestHistory.length - 1 - index] </div>
? "▼" {expandedRequests[requestHistory.length - 1 - index] && (
: "▶"} <>
</span> <div className="mt-2">
<div className="flex justify-between items-center mb-1">
<span className="font-semibold text-blue-600">
Request:
</span>
<button
onClick={() => copyToClipboard(request.request)}
className="text-blue-500 hover:text-blue-700"
>
<Copy size={16} />
</button>
</div>
<pre className="whitespace-pre-wrap break-words bg-blue-50 p-2 rounded">
{JSON.stringify(JSON.parse(request.request), null, 2)}
</pre>
</div> </div>
{expandedRequests[requestHistory.length - 1 - index] && ( {request.response && (
<>
<div className="mt-2">
<div className="flex justify-between items-center mb-1">
<span className="font-semibold text-blue-600">
Request:
</span>
<button
onClick={() => copyToClipboard(request.request)}
className="text-blue-500 hover:text-blue-700"
>
<Copy size={16} />
</button>
</div>
<pre className="whitespace-pre-wrap break-words bg-blue-50 p-2 rounded">
{JSON.stringify(JSON.parse(request.request), null, 2)}
</pre>
</div>
{request.response && (
<div className="mt-2">
<div className="flex justify-between items-center mb-1">
<span className="font-semibold text-green-600">
Response:
</span>
<button
onClick={() => copyToClipboard(request.response!)}
className="text-blue-500 hover:text-blue-700"
>
<Copy size={16} />
</button>
</div>
<pre className="whitespace-pre-wrap break-words bg-green-50 p-2 rounded">
{JSON.stringify(
JSON.parse(request.response),
null,
2,
)}
</pre>
</div>
)}
</>
)}
</li>
))}
</ul>
)}
</div>
<div className="flex-1 overflow-y-auto">
<h2 className="text-lg font-semibold mb-4">Server Notifications</h2>
{serverNotifications.length === 0 ? (
<p className="text-sm text-gray-500 italic">No notifications yet</p>
) : (
<ul className="space-y-3">
{serverNotifications
.slice()
.reverse()
.map((notification, index) => (
<li
key={index}
className="text-sm text-gray-600 bg-gray-100 p-2 rounded"
>
<div
className="flex justify-between items-center cursor-pointer"
onClick={() => toggleNotificationExpansion(index)}
>
<span className="font-mono">
{serverNotifications.length - index}.{" "}
{notification.method}
</span>
<span>{expandedNotifications[index] ? "▼" : "▶"}</span>
</div>
{expandedNotifications[index] && (
<div className="mt-2"> <div className="mt-2">
<div className="flex justify-between items-center mb-1"> <div className="flex justify-between items-center mb-1">
<span className="font-semibold text-purple-600"> <span className="font-semibold text-green-600">
Details: Response:
</span> </span>
<button <button
onClick={() => onClick={() => copyToClipboard(request.response!)}
copyToClipboard(JSON.stringify(notification))
}
className="text-blue-500 hover:text-blue-700" className="text-blue-500 hover:text-blue-700"
> >
<Copy size={16} /> <Copy size={16} />
</button> </button>
</div> </div>
<pre className="whitespace-pre-wrap break-words bg-purple-50 p-2 rounded"> <pre className="whitespace-pre-wrap break-words bg-green-50 p-2 rounded">
{JSON.stringify(notification, null, 2)} {JSON.stringify(JSON.parse(request.response), null, 2)}
</pre> </pre>
</div> </div>
)} )}
</li> </>
))} )}
</ul> </li>
)} ))}
</div> </ul>
</div> </div>
); );
}; };
export default HistoryAndNotifications; export default History;

View File

@@ -7,7 +7,6 @@ type ListPaneProps<T> = {
renderItem: (item: T) => React.ReactNode; renderItem: (item: T) => React.ReactNode;
title: string; title: string;
buttonText: string; buttonText: string;
isButtonDisabled?: boolean;
}; };
const ListPane = <T extends object>({ const ListPane = <T extends object>({
@@ -17,22 +16,16 @@ const ListPane = <T extends object>({
renderItem, renderItem,
title, title,
buttonText, buttonText,
isButtonDisabled,
}: ListPaneProps<T>) => ( }: ListPaneProps<T>) => (
<div className="bg-white rounded-lg shadow"> <div className="bg-white rounded-lg shadow">
<div className="p-4 border-b border-gray-200"> <div className="p-4 border-b border-gray-200">
<h3 className="font-semibold">{title}</h3> <h3 className="font-semibold">{title}</h3>
</div> </div>
<div className="p-4"> <div className="p-4">
<Button <Button variant="outline" className="w-full mb-4" onClick={listItems}>
variant="outline"
className="w-full mb-4"
onClick={listItems}
disabled={isButtonDisabled}
>
{buttonText} {buttonText}
</Button> </Button>
<div className="space-y-2 overflow-y-auto max-h-96"> <div className="space-y-2">
{items.map((item, index) => ( {items.map((item, index) => (
<div <div
key={index} key={index}

View File

@@ -0,0 +1,33 @@
import { Bell } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { TabsContent } from "@/components/ui/tabs";
const NotificationsTab = () => (
<TabsContent value="notifications" className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div className="space-y-4">
<div className="flex space-x-2">
<Input placeholder="Notification method" />
<Button>
<Bell className="w-4 h-4 mr-2" />
Send
</Button>
</div>
<Textarea
placeholder="Notification parameters (JSON)"
className="h-64 font-mono"
/>
</div>
<div className="bg-white rounded-lg shadow p-4">
<h3 className="font-semibold mb-4">Recent Notifications</h3>
<div className="space-y-2">
{/* Notification history would go here */}
</div>
</div>
</div>
</TabsContent>
);
export default NotificationsTab;

View File

@@ -1,21 +0,0 @@
import { TabsContent } from "@/components/ui/tabs";
import { Button } from "@/components/ui/button";
const PingTab = ({ onPingClick }: { onPingClick: () => void }) => {
return (
<TabsContent value="ping" className="grid grid-cols-2 gap-4">
<div className="col-span-2 flex justify-center items-center">
<Button
onClick={onPingClick}
className="bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white font-bold py-6 px-12 rounded-full shadow-lg transform transition duration-300 hover:scale-110 focus:outline-none focus:ring-4 focus:ring-purple-300 animate-pulse"
>
<span className="text-3xl mr-2">🚀</span>
MEGA PING
<span className="text-3xl ml-2">💥</span>
</Button>
</div>
</TabsContent>
);
};
export default PingTab;

View File

@@ -1,12 +1,11 @@
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { TabsContent } from "@/components/ui/tabs";
import { Textarea } from "@/components/ui/textarea";
import { ListPromptsResult } from "@modelcontextprotocol/sdk/types.js";
import { AlertCircle } from "lucide-react"; import { AlertCircle } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { TabsContent } from "@/components/ui/tabs";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { useState } from "react"; import { useState } from "react";
import { Label } from "@/components/ui/label";
import ListPane from "./ListPane"; import ListPane from "./ListPane";
export type Prompt = { export type Prompt = {
@@ -26,7 +25,6 @@ const PromptsTab = ({
selectedPrompt, selectedPrompt,
setSelectedPrompt, setSelectedPrompt,
promptContent, promptContent,
nextCursor,
error, error,
}: { }: {
prompts: Prompt[]; prompts: Prompt[];
@@ -35,7 +33,6 @@ const PromptsTab = ({
selectedPrompt: Prompt | null; selectedPrompt: Prompt | null;
setSelectedPrompt: (prompt: Prompt) => void; setSelectedPrompt: (prompt: Prompt) => void;
promptContent: string; promptContent: string;
nextCursor: ListPromptsResult["nextCursor"];
error: string | null; error: string | null;
}) => { }) => {
const [promptArgs, setPromptArgs] = useState<Record<string, string>>({}); const [promptArgs, setPromptArgs] = useState<Record<string, string>>({});
@@ -66,8 +63,7 @@ const PromptsTab = ({
</> </>
)} )}
title="Prompts" title="Prompts"
buttonText={nextCursor ? "List More Prompts" : "List Prompts"} buttonText="List Prompts"
isButtonDisabled={!nextCursor && prompts.length > 0}
/> />
<div className="bg-white rounded-lg shadow"> <div className="bg-white rounded-lg shadow">

View File

@@ -1,8 +1,8 @@
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { FileText, ChevronRight, AlertCircle, RefreshCw } from "lucide-react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { TabsContent } from "@/components/ui/tabs"; import { TabsContent } from "@/components/ui/tabs";
import { ListResourcesResult, Resource } from "@modelcontextprotocol/sdk/types.js"; import { Resource } from "mcp-typescript/types.js";
import { AlertCircle, ChevronRight, FileText, RefreshCw } from "lucide-react";
import ListPane from "./ListPane"; import ListPane from "./ListPane";
const ResourcesTab = ({ const ResourcesTab = ({
@@ -12,16 +12,14 @@ const ResourcesTab = ({
selectedResource, selectedResource,
setSelectedResource, setSelectedResource,
resourceContent, resourceContent,
nextCursor,
error, error,
}: { }: {
resources: Resource[]; resources: Resource[];
listResources: () => void; listResources: () => void;
readResource: (uri: string) => void; readResource: (uri: URL) => void;
selectedResource: Resource | null; selectedResource: Resource | null;
setSelectedResource: (resource: Resource) => void; setSelectedResource: (resource: Resource) => void;
resourceContent: string; resourceContent: string;
nextCursor: ListResourcesResult["nextCursor"];
error: string | null; error: string | null;
}) => ( }) => (
<TabsContent value="resources" className="grid grid-cols-2 gap-4"> <TabsContent value="resources" className="grid grid-cols-2 gap-4">
@@ -42,8 +40,7 @@ const ResourcesTab = ({
</div> </div>
)} )}
title="Resources" title="Resources"
buttonText={nextCursor ? "List More Resources" : "List Resources"} buttonText="List Resources"
isButtonDisabled={!nextCursor && resources.length > 0}
/> />
<div className="bg-white rounded-lg shadow"> <div className="bg-white rounded-lg shadow">

View File

@@ -1,65 +0,0 @@
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { TabsContent } from "@/components/ui/tabs";
import {
CreateMessageRequest,
CreateMessageResult,
} from "@modelcontextprotocol/sdk/types.js";
export type PendingRequest = {
id: number;
request: CreateMessageRequest;
};
export type Props = {
pendingRequests: PendingRequest[];
onApprove: (id: number, result: CreateMessageResult) => void;
onReject: (id: number) => void;
};
const SamplingTab = ({ pendingRequests, onApprove, onReject }: Props) => {
const handleApprove = (id: number) => {
// For now, just return a stub response
onApprove(id, {
model: "stub-model",
stopReason: "endTurn",
role: "assistant",
content: {
type: "text",
text: "This is a stub response.",
},
});
};
return (
<TabsContent value="sampling" className="h-96">
<Alert>
<AlertDescription>
When the server requests LLM sampling, requests will appear here for
approval.
</AlertDescription>
</Alert>
<div className="mt-4 space-y-4">
<h3 className="text-lg font-semibold">Recent Requests</h3>
{pendingRequests.map((request) => (
<div key={request.id} className="p-4 border rounded-lg space-y-4">
<pre className="bg-gray-50 p-2 rounded">
{JSON.stringify(request.request, null, 2)}
</pre>
<div className="flex space-x-2">
<Button onClick={() => handleApprove(request.id)}>Approve</Button>
<Button variant="outline" onClick={() => onReject(request.id)}>
Reject
</Button>
</div>
</div>
))}
{pendingRequests.length === 0 && (
<p className="text-gray-500">No pending requests</p>
)}
</div>
</TabsContent>
);
};
export default SamplingTab;

View File

@@ -1,11 +1,11 @@
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { TabsContent } from "@/components/ui/tabs";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Send, AlertCircle } from "lucide-react";
import { TabsContent } from "@/components/ui/tabs"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { ListToolsResult, Tool } from "@modelcontextprotocol/sdk/types.js"; import { Tool } from "mcp-typescript/types.js";
import { AlertCircle, Send } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { Label } from "@/components/ui/label";
import ListPane from "./ListPane"; import ListPane from "./ListPane";
const ToolsTab = ({ const ToolsTab = ({
@@ -15,7 +15,6 @@ const ToolsTab = ({
selectedTool, selectedTool,
setSelectedTool, setSelectedTool,
toolResult, toolResult,
nextCursor,
error, error,
}: { }: {
tools: Tool[]; tools: Tool[];
@@ -24,7 +23,6 @@ const ToolsTab = ({
selectedTool: Tool | null; selectedTool: Tool | null;
setSelectedTool: (tool: Tool) => void; setSelectedTool: (tool: Tool) => void;
toolResult: string; toolResult: string;
nextCursor: ListToolsResult["nextCursor"];
error: string | null; error: string | null;
}) => { }) => {
const [params, setParams] = useState<Record<string, unknown>>({}); const [params, setParams] = useState<Record<string, unknown>>({});
@@ -38,14 +36,11 @@ const ToolsTab = ({
renderItem={(tool) => ( renderItem={(tool) => (
<> <>
<span className="flex-1">{tool.name}</span> <span className="flex-1">{tool.name}</span>
<span className="text-sm text-gray-500 text-right"> <span className="text-sm text-gray-500">{tool.description}</span>
{tool.description}
</span>
</> </>
)} )}
title="Tools" title="Tools"
buttonText={nextCursor ? "List More Tools" : "List Tools"} buttonText="List Tools"
isButtonDisabled={!nextCursor && tools.length > 0}
/> />
<div className="bg-white rounded-lg shadow"> <div className="bg-white rounded-lg shadow">
@@ -66,7 +61,7 @@ const ToolsTab = ({
<p className="text-sm text-gray-600"> <p className="text-sm text-gray-600">
{selectedTool.description} {selectedTool.description}
</p> </p>
{Object.entries(selectedTool.inputSchema.properties ?? []).map( {Object.entries(selectedTool.inputSchema.properties).map(
([key, value]) => ( ([key, value]) => (
<div key={key}> <div key={key}>
<Label <Label
@@ -76,17 +71,14 @@ const ToolsTab = ({
{key} {key}
</Label> </Label>
<Input <Input
// @ts-expect-error value type is currently unknown
type={value.type === "number" ? "number" : "text"} type={value.type === "number" ? "number" : "text"}
id={key} id={key}
name={key} name={key}
// @ts-expect-error value type is currently unknown
placeholder={value.description} placeholder={value.description}
onChange={(e) => onChange={(e) =>
setParams({ setParams({
...params, ...params,
[key]: [key]:
// @ts-expect-error value type is currently unknown
value.type === "number" value.type === "number"
? Number(e.target.value) ? Number(e.target.value)
: e.target.value, : e.target.value,

View File

@@ -0,0 +1 @@
{"root":["./vite.config.ts"],"version":"5.6.2"}

5815
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,19 +3,20 @@
"private": true, "private": true,
"version": "0.0.1", "version": "0.0.1",
"type": "module", "type": "module",
"workspaces": [ "workspaces": ["client", "server"],
"client",
"server"
],
"scripts": { "scripts": {
"dev": "concurrently \"cd client && npm run dev\" \"cd server && npm run dev\"", "dev": "concurrently \"cd client && yarn dev\" \"cd server && yarn dev\"",
"build-server": "cd server && npm run build", "build-server": "cd server && yarn build",
"build-client": "cd client && npm run build", "build-client": "cd client && yarn build",
"build": "npm run build-server && npm run build-client", "build": "yarn build-server && yarn build-client",
"start-server": "cd server && npm run start", "start-server": "cd server && yarn start",
"start-client": "cd client && npm run preview", "start-client": "cd client && yarn preview",
"start": "concurrently \"npm run start-server\" \"npm run start-client\"", "start": "concurrently \"yarn start-server\" \"yarn start-client\"",
"prettier-fix": "prettier --write ." "prettier-fix": "prettier --write .",
"update:mcp": "git subtree pull --prefix=packages/mcp-typescript https://github.com/modelcontextprotocol/typescript-sdk.git main --squash"
},
"dependencies": {
"mcp-typescript": "file:packages/mcp-typescript"
}, },
"devDependencies": { "devDependencies": {
"concurrently": "^9.0.1", "concurrently": "^9.0.1",

1
packages/mcp-typescript/.gitattributes generated vendored Normal file
View File

@@ -0,0 +1 @@
dist/**/* linguist-generated=true

26
packages/mcp-typescript/.github/workflows/main.yml generated vendored Normal file
View File

@@ -0,0 +1,26 @@
on:
push:
branches:
- main
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
cache: yarn
- run: yarn install --immutable
- run: yarn build
- name: Verify that `yarn build` did not change outputs
run: git diff --exit-code
- run: yarn test
- run: yarn lint

131
packages/mcp-typescript/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,131 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
.DS_Store

2
packages/mcp-typescript/README.md generated Normal file
View File

@@ -0,0 +1,2 @@
# mcp-typescript
TypeScript implementation of the Model Context Protocol

2
packages/mcp-typescript/dist/cli.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=cli.d.ts.map

1
packages/mcp-typescript/dist/cli.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}

119
packages/mcp-typescript/dist/cli.js generated vendored Normal file
View File

@@ -0,0 +1,119 @@
import EventSource from "eventsource";
import WebSocket from "ws";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
global.EventSource = EventSource;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
global.WebSocket = WebSocket;
import express from "express";
import { Client } from "./client/index.js";
import { SSEClientTransport } from "./client/sse.js";
import { Server } from "./server/index.js";
import { SSEServerTransport } from "./server/sse.js";
import { WebSocketClientTransport } from "./client/websocket.js";
import { StdioClientTransport } from "./client/stdio.js";
import { StdioServerTransport } from "./server/stdio.js";
async function runClient(url_or_command, args) {
const client = new Client({
name: "mcp-typescript test client",
version: "0.1.0",
});
let clientTransport;
let url = undefined;
try {
url = new URL(url_or_command);
}
catch (_a) {
// Ignore
}
if ((url === null || url === void 0 ? void 0 : url.protocol) === "http:" || (url === null || url === void 0 ? void 0 : url.protocol) === "https:") {
clientTransport = new SSEClientTransport();
await clientTransport.connect(new URL(url_or_command));
}
else if ((url === null || url === void 0 ? void 0 : url.protocol) === "ws:" || (url === null || url === void 0 ? void 0 : url.protocol) === "wss:") {
clientTransport = new WebSocketClientTransport();
await clientTransport.connect(new URL(url_or_command));
}
else {
clientTransport = new StdioClientTransport();
await clientTransport.spawn({
command: url_or_command,
args,
});
}
console.log("Connected to server.");
await client.connect(clientTransport);
console.log("Initialized.");
await client.close();
console.log("Closed.");
}
async function runServer(port) {
if (port !== null) {
const app = express();
let servers = [];
app.get("/sse", async (req, res) => {
console.log("Got new SSE connection");
const transport = new SSEServerTransport("/message");
const server = new Server({
name: "mcp-typescript test server",
version: "0.1.0",
});
servers.push(server);
server.onclose = () => {
console.log("SSE connection closed");
servers = servers.filter((s) => s !== server);
};
await transport.connectSSE(req, res);
await server.connect(transport);
});
app.post("/message", async (req, res) => {
console.log("Received message");
const sessionId = req.query.sessionId;
const transport = servers
.map((s) => s.transport)
.find((t) => t.sessionId === sessionId);
if (!transport) {
res.status(404).send("Session not found");
return;
}
await transport.handlePostMessage(req, res);
});
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}/sse`);
});
}
else {
const server = new Server({
name: "mcp-typescript test server",
version: "0.1.0",
});
const transport = new StdioServerTransport();
await transport.start();
await server.connect(transport);
console.log("Server running on stdio");
}
}
const args = process.argv.slice(2);
const command = args[0];
switch (command) {
case "client":
if (args.length < 2) {
console.error("Usage: client <server_url_or_command> [args...]");
process.exit(1);
}
runClient(args[1], args.slice(2)).catch((error) => {
console.error(error);
process.exit(1);
});
break;
case "server": {
const port = args[1] ? parseInt(args[1]) : null;
runServer(port).catch((error) => {
console.error(error);
process.exit(1);
});
break;
}
default:
console.error("Unrecognized command:", command);
}
//# sourceMappingURL=cli.js.map

1
packages/mcp-typescript/dist/cli.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}

27
packages/mcp-typescript/dist/client/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,27 @@
import { Protocol } from "../shared/protocol.js";
import { Transport } from "../shared/transport.js";
import { ClientNotification, ClientRequest, ClientResult, Implementation, ServerCapabilities } from "../types.js";
/**
* An MCP client on top of a pluggable transport.
*
* The client will automatically begin the initialization flow with the server when connect() is called.
*/
export declare class Client extends Protocol<ClientRequest, ClientNotification, ClientResult> {
private _clientInfo;
private _serverCapabilities?;
private _serverVersion?;
/**
* Initializes this client with the given name and version information.
*/
constructor(_clientInfo: Implementation);
connect(transport: Transport): Promise<void>;
/**
* After initialization has completed, this will be populated with the server's reported capabilities.
*/
getServerCapabilities(): ServerCapabilities | undefined;
/**
* After initialization has completed, this will be populated with information about the server's name and version.
*/
getServerVersion(): Implementation | undefined;
}
//# sourceMappingURL=index.d.ts.map

1
packages/mcp-typescript/dist/client/index.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,cAAc,EAGd,kBAAkB,EACnB,MAAM,aAAa,CAAC;AAErB;;;;GAIG;AACH,qBAAa,MAAO,SAAQ,QAAQ,CAClC,aAAa,EACb,kBAAkB,EAClB,YAAY,CACb;IAOa,OAAO,CAAC,WAAW;IAN/B,OAAO,CAAC,mBAAmB,CAAC,CAAqB;IACjD,OAAO,CAAC,cAAc,CAAC,CAAiB;IAExC;;OAEG;gBACiB,WAAW,EAAE,cAAc;IAIhC,OAAO,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAiC3D;;OAEG;IACH,qBAAqB,IAAI,kBAAkB,GAAG,SAAS;IAIvD;;OAEG;IACH,gBAAgB,IAAI,cAAc,GAAG,SAAS;CAG/C"}

51
packages/mcp-typescript/dist/client/index.js generated vendored Normal file
View File

@@ -0,0 +1,51 @@
import { Protocol } from "../shared/protocol.js";
import { InitializeResultSchema, PROTOCOL_VERSION, } from "../types.js";
/**
* An MCP client on top of a pluggable transport.
*
* The client will automatically begin the initialization flow with the server when connect() is called.
*/
export class Client extends Protocol {
/**
* Initializes this client with the given name and version information.
*/
constructor(_clientInfo) {
super();
this._clientInfo = _clientInfo;
}
async connect(transport) {
await super.connect(transport);
const result = await this.request({
method: "initialize",
params: {
protocolVersion: PROTOCOL_VERSION,
capabilities: {},
clientInfo: this._clientInfo,
},
}, InitializeResultSchema);
if (result === undefined) {
throw new Error(`Server sent invalid initialize result: ${result}`);
}
if (result.protocolVersion !== PROTOCOL_VERSION) {
throw new Error(`Server's protocol version is not supported: ${result.protocolVersion}`);
}
this._serverCapabilities = result.capabilities;
this._serverVersion = result.serverInfo;
await this.notification({
method: "notifications/initialized",
});
}
/**
* After initialization has completed, this will be populated with the server's reported capabilities.
*/
getServerCapabilities() {
return this._serverCapabilities;
}
/**
* After initialization has completed, this will be populated with information about the server's name and version.
*/
getServerVersion() {
return this._serverVersion;
}
}
//# sourceMappingURL=index.js.map

1
packages/mcp-typescript/dist/client/index.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,OAAO,EAKL,sBAAsB,EACtB,gBAAgB,GAEjB,MAAM,aAAa,CAAC;AAErB;;;;GAIG;AACH,MAAM,OAAO,MAAO,SAAQ,QAI3B;IAIC;;OAEG;IACH,YAAoB,WAA2B;QAC7C,KAAK,EAAE,CAAC;QADU,gBAAW,GAAX,WAAW,CAAgB;IAE/C,CAAC;IAEQ,KAAK,CAAC,OAAO,CAAC,SAAoB;QACzC,MAAM,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAE/B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAC/B;YACE,MAAM,EAAE,YAAY;YACpB,MAAM,EAAE;gBACN,eAAe,EAAE,gBAAgB;gBACjC,YAAY,EAAE,EAAE;gBAChB,UAAU,EAAE,IAAI,CAAC,WAAW;aAC7B;SACF,EACD,sBAAsB,CACvB,CAAC;QAEF,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,0CAA0C,MAAM,EAAE,CAAC,CAAC;QACtE,CAAC;QAED,IAAI,MAAM,CAAC,eAAe,KAAK,gBAAgB,EAAE,CAAC;YAChD,MAAM,IAAI,KAAK,CACb,+CAA+C,MAAM,CAAC,eAAe,EAAE,CACxE,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,mBAAmB,GAAG,MAAM,CAAC,YAAY,CAAC;QAC/C,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC;QAExC,MAAM,IAAI,CAAC,YAAY,CAAC;YACtB,MAAM,EAAE,2BAA2B;SACpC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,qBAAqB;QACnB,OAAO,IAAI,CAAC,mBAAmB,CAAC;IAClC,CAAC;IAED;;OAEG;IACH,gBAAgB;QACd,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;CACF"}

20
packages/mcp-typescript/dist/client/sse.d.ts generated vendored Normal file
View File

@@ -0,0 +1,20 @@
import { Transport } from "../shared/transport.js";
import { JSONRPCMessage } from "../types.js";
/**
* Client transport for SSE: this will connect to a server using Server-Sent Events for receiving
* messages and make separate POST requests for sending messages.
*
* This uses the EventSource API in browsers. You can install the `eventsource` package for Node.js.
*/
export declare class SSEClientTransport implements Transport {
private _eventSource?;
private _endpoint?;
private _abortController?;
onclose?: () => void;
onerror?: (error: Error) => void;
onmessage?: (message: JSONRPCMessage) => void;
connect(url: URL): Promise<void>;
close(): Promise<void>;
send(message: JSONRPCMessage): Promise<void>;
}
//# sourceMappingURL=sse.d.ts.map

1
packages/mcp-typescript/dist/client/sse.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/client/sse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAwB,MAAM,aAAa,CAAC;AAEnE;;;;;GAKG;AACH,qBAAa,kBAAmB,YAAW,SAAS;IAClD,OAAO,CAAC,YAAY,CAAC,CAAc;IACnC,OAAO,CAAC,SAAS,CAAC,CAAM;IACxB,OAAO,CAAC,gBAAgB,CAAC,CAAkB;IAE3C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAE9C,OAAO,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAmD1B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAMtB,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;CA0BnD"}

85
packages/mcp-typescript/dist/client/sse.js generated vendored Normal file
View File

@@ -0,0 +1,85 @@
import { JSONRPCMessageSchema } from "../types.js";
/**
* Client transport for SSE: this will connect to a server using Server-Sent Events for receiving
* messages and make separate POST requests for sending messages.
*
* This uses the EventSource API in browsers. You can install the `eventsource` package for Node.js.
*/
export class SSEClientTransport {
connect(url) {
return new Promise((resolve, reject) => {
this._eventSource = new EventSource(url.href);
this._abortController = new AbortController();
this._eventSource.onerror = (event) => {
var _a;
const error = new Error(`SSE error: ${JSON.stringify(event)}`);
reject(error);
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
};
this._eventSource.onopen = () => {
// The connection is open, but we need to wait for the endpoint to be received.
};
this._eventSource.addEventListener("endpoint", (event) => {
var _a;
const messageEvent = event;
try {
this._endpoint = new URL(messageEvent.data, url);
if (this._endpoint.origin !== url.origin) {
throw new Error(`Endpoint origin does not match connection origin: ${this._endpoint.origin}`);
}
}
catch (error) {
reject(error);
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
void this.close();
return;
}
resolve();
});
this._eventSource.onmessage = (event) => {
var _a, _b;
const messageEvent = event;
let message;
try {
message = JSONRPCMessageSchema.parse(JSON.parse(messageEvent.data));
}
catch (error) {
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
return;
}
(_b = this.onmessage) === null || _b === void 0 ? void 0 : _b.call(this, message);
};
});
}
async close() {
var _a, _b, _c;
(_a = this._abortController) === null || _a === void 0 ? void 0 : _a.abort();
(_b = this._eventSource) === null || _b === void 0 ? void 0 : _b.close();
(_c = this.onclose) === null || _c === void 0 ? void 0 : _c.call(this);
}
async send(message) {
var _a, _b;
if (!this._endpoint) {
throw new Error("Not connected");
}
try {
const response = await fetch(this._endpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(message),
signal: (_a = this._abortController) === null || _a === void 0 ? void 0 : _a.signal,
});
if (!response.ok) {
const text = await response.text().catch(() => null);
throw new Error(`Error POSTing to endpoint (HTTP ${response.status}): ${text}`);
}
}
catch (error) {
(_b = this.onerror) === null || _b === void 0 ? void 0 : _b.call(this, error);
throw error;
}
}
}
//# sourceMappingURL=sse.js.map

1
packages/mcp-typescript/dist/client/sse.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"sse.js","sourceRoot":"","sources":["../../src/client/sse.ts"],"names":[],"mappings":"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"}

39
packages/mcp-typescript/dist/client/stdio.d.ts generated vendored Normal file
View File

@@ -0,0 +1,39 @@
import { JSONRPCMessage } from "../types.js";
import { Transport } from "../shared/transport.js";
export type StdioServerParameters = {
/**
* The executable to run to start the server.
*/
command: string;
/**
* Command line arguments to pass to the executable.
*/
args?: string[];
/**
* The environment to use when spawning the process.
*
* The environment is NOT inherited from the parent process by default.
*/
env?: object;
};
/**
* Client transport for stdio: this will connect to a server by spawning a process and communicating with it over stdin/stdout.
*
* This transport is only available in Node.js environments.
*/
export declare class StdioClientTransport implements Transport {
private _process?;
private _abortController;
private _readBuffer;
onclose?: () => void;
onerror?: (error: Error) => void;
onmessage?: (message: JSONRPCMessage) => void;
/**
* Spawns the server process and prepare to communicate with it.
*/
spawn(server: StdioServerParameters): Promise<void>;
private processReadBuffer;
close(): Promise<void>;
send(message: JSONRPCMessage): Promise<void>;
}
//# sourceMappingURL=stdio.d.ts.map

1
packages/mcp-typescript/dist/client/stdio.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../src/client/stdio.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEnD,MAAM,MAAM,qBAAqB,GAAG;IAClC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAEhB;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;GAIG;AACH,qBAAa,oBAAqB,YAAW,SAAS;IACpD,OAAO,CAAC,QAAQ,CAAC,CAAe;IAChC,OAAO,CAAC,gBAAgB,CAA0C;IAClE,OAAO,CAAC,WAAW,CAAgC;IAEnD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAE9C;;OAEG;IACH,KAAK,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IA4CnD,OAAO,CAAC,iBAAiB;IAenB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;CAc7C"}

93
packages/mcp-typescript/dist/client/stdio.js generated vendored Normal file
View File

@@ -0,0 +1,93 @@
import { spawn } from "node:child_process";
import { ReadBuffer, serializeMessage } from "../shared/stdio.js";
/**
* Client transport for stdio: this will connect to a server by spawning a process and communicating with it over stdin/stdout.
*
* This transport is only available in Node.js environments.
*/
export class StdioClientTransport {
constructor() {
this._abortController = new AbortController();
this._readBuffer = new ReadBuffer();
}
/**
* Spawns the server process and prepare to communicate with it.
*/
spawn(server) {
return new Promise((resolve, reject) => {
var _a, _b, _c, _d;
this._process = spawn(server.command, (_a = server.args) !== null && _a !== void 0 ? _a : [], {
// The parent process may have sensitive secrets in its env, so don't inherit it automatically.
env: server.env === undefined ? {} : { ...server.env },
stdio: ["pipe", "pipe", "inherit"],
signal: this._abortController.signal,
});
this._process.on("error", (error) => {
var _a, _b;
if (error.name === "AbortError") {
// Expected when close() is called.
(_a = this.onclose) === null || _a === void 0 ? void 0 : _a.call(this);
return;
}
reject(error);
(_b = this.onerror) === null || _b === void 0 ? void 0 : _b.call(this, error);
});
this._process.on("spawn", () => {
resolve();
});
this._process.on("close", (_code) => {
var _a;
this._process = undefined;
(_a = this.onclose) === null || _a === void 0 ? void 0 : _a.call(this);
});
(_b = this._process.stdin) === null || _b === void 0 ? void 0 : _b.on("error", (error) => {
var _a;
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
});
(_c = this._process.stdout) === null || _c === void 0 ? void 0 : _c.on("data", (chunk) => {
this._readBuffer.append(chunk);
this.processReadBuffer();
});
(_d = this._process.stdout) === null || _d === void 0 ? void 0 : _d.on("error", (error) => {
var _a;
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
});
});
}
processReadBuffer() {
var _a, _b;
while (true) {
try {
const message = this._readBuffer.readMessage();
if (message === null) {
break;
}
(_a = this.onmessage) === null || _a === void 0 ? void 0 : _a.call(this, message);
}
catch (error) {
(_b = this.onerror) === null || _b === void 0 ? void 0 : _b.call(this, error);
}
}
}
async close() {
this._abortController.abort();
this._process = undefined;
this._readBuffer.clear();
}
send(message) {
return new Promise((resolve) => {
var _a;
if (!((_a = this._process) === null || _a === void 0 ? void 0 : _a.stdin)) {
throw new Error("Not connected");
}
const json = serializeMessage(message);
if (this._process.stdin.write(json)) {
resolve();
}
else {
this._process.stdin.once("drain", resolve);
}
});
}
}
//# sourceMappingURL=stdio.js.map

1
packages/mcp-typescript/dist/client/stdio.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/client/stdio.ts"],"names":[],"mappings":"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"}

2
packages/mcp-typescript/dist/client/stdio.test.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=stdio.test.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"stdio.test.d.ts","sourceRoot":"","sources":["../../src/client/stdio.test.ts"],"names":[],"mappings":""}

51
packages/mcp-typescript/dist/client/stdio.test.js generated vendored Normal file
View File

@@ -0,0 +1,51 @@
import { StdioClientTransport } from "./stdio.js";
const serverParameters = {
command: "/usr/bin/tee",
};
test("should start then close cleanly", async () => {
const client = new StdioClientTransport();
client.onerror = (error) => {
throw error;
};
let didClose = false;
client.onclose = () => {
didClose = true;
};
await client.spawn(serverParameters);
expect(didClose).toBeFalsy();
await client.close();
expect(didClose).toBeTruthy();
});
test("should read messages", async () => {
const client = new StdioClientTransport();
client.onerror = (error) => {
throw error;
};
const messages = [
{
jsonrpc: "2.0",
id: 1,
method: "ping",
},
{
jsonrpc: "2.0",
method: "notifications/initialized",
},
];
const readMessages = [];
const finished = new Promise((resolve) => {
client.onmessage = (message) => {
readMessages.push(message);
if (JSON.stringify(message) === JSON.stringify(messages[1])) {
resolve();
}
};
});
await client.spawn(serverParameters);
await client.send(messages[0]);
await client.send(messages[1]);
await finished;
expect(readMessages).toEqual(messages);
await client.close();
});
//# sourceMappingURL=stdio.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"stdio.test.js","sourceRoot":"","sources":["../../src/client/stdio.test.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAyB,MAAM,YAAY,CAAC;AAEzE,MAAM,gBAAgB,GAA0B;IAC9C,OAAO,EAAE,cAAc;CACxB,CAAC;AAEF,IAAI,CAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;IACjD,MAAM,MAAM,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC1C,MAAM,CAAC,OAAO,GAAG,CAAC,KAAK,EAAE,EAAE;QACzB,MAAM,KAAK,CAAC;IACd,CAAC,CAAC;IAEF,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,MAAM,CAAC,OAAO,GAAG,GAAG,EAAE;QACpB,QAAQ,GAAG,IAAI,CAAC;IAClB,CAAC,CAAC;IAEF,MAAM,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IACrC,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,EAAE,CAAC;IAC7B,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACrB,MAAM,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,CAAC;AAChC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;IACtC,MAAM,MAAM,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC1C,MAAM,CAAC,OAAO,GAAG,CAAC,KAAK,EAAE,EAAE;QACzB,MAAM,KAAK,CAAC;IACd,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAqB;QACjC;YACE,OAAO,EAAE,KAAK;YACd,EAAE,EAAE,CAAC;YACL,MAAM,EAAE,MAAM;SACf;QACD;YACE,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,2BAA2B;SACpC;KACF,CAAC;IAEF,MAAM,YAAY,GAAqB,EAAE,CAAC;IAC1C,MAAM,QAAQ,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAC7C,MAAM,CAAC,SAAS,GAAG,CAAC,OAAO,EAAE,EAAE;YAC7B,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAE3B,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC5D,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IACrC,MAAM,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,QAAQ,CAAC;IACf,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAEvC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;AACvB,CAAC,CAAC,CAAC"}

15
packages/mcp-typescript/dist/client/websocket.d.ts generated vendored Normal file
View File

@@ -0,0 +1,15 @@
import { Transport } from "../shared/transport.js";
import { JSONRPCMessage } from "../types.js";
/**
* Client transport for WebSocket: this will connect to a server over the WebSocket protocol.
*/
export declare class WebSocketClientTransport implements Transport {
private _socket?;
onclose?: () => void;
onerror?: (error: Error) => void;
onmessage?: (message: JSONRPCMessage) => void;
connect(url: URL): Promise<void>;
close(): Promise<void>;
send(message: JSONRPCMessage): Promise<void>;
}
//# sourceMappingURL=websocket.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../../src/client/websocket.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAwB,MAAM,aAAa,CAAC;AAInE;;GAEG;AACH,qBAAa,wBAAyB,YAAW,SAAS;IACxD,OAAO,CAAC,OAAO,CAAC,CAAY;IAE5B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAE9C,OAAO,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAmC1B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;CAW7C"}

55
packages/mcp-typescript/dist/client/websocket.js generated vendored Normal file
View File

@@ -0,0 +1,55 @@
import { JSONRPCMessageSchema } from "../types.js";
const SUBPROTOCOL = "mcp";
/**
* Client transport for WebSocket: this will connect to a server over the WebSocket protocol.
*/
export class WebSocketClientTransport {
connect(url) {
return new Promise((resolve, reject) => {
this._socket = new WebSocket(url, SUBPROTOCOL);
this._socket.onerror = (event) => {
var _a;
const error = "error" in event
? event.error
: new Error(`WebSocket error: ${JSON.stringify(event)}`);
reject(error);
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
};
this._socket.onopen = () => {
resolve();
};
this._socket.onclose = () => {
var _a;
(_a = this.onclose) === null || _a === void 0 ? void 0 : _a.call(this);
};
this._socket.onmessage = (event) => {
var _a, _b;
let message;
try {
message = JSONRPCMessageSchema.parse(JSON.parse(event.data));
}
catch (error) {
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
return;
}
(_b = this.onmessage) === null || _b === void 0 ? void 0 : _b.call(this, message);
};
});
}
async close() {
var _a;
(_a = this._socket) === null || _a === void 0 ? void 0 : _a.close();
}
send(message) {
return new Promise((resolve, reject) => {
var _a;
if (!this._socket) {
reject(new Error("Not connected"));
return;
}
(_a = this._socket) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify(message));
resolve();
});
}
}
//# sourceMappingURL=websocket.js.map

1
packages/mcp-typescript/dist/client/websocket.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"websocket.js","sourceRoot":"","sources":["../../src/client/websocket.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEnE,MAAM,WAAW,GAAG,KAAK,CAAC;AAE1B;;GAEG;AACH,MAAM,OAAO,wBAAwB;IAOnC,OAAO,CAAC,GAAQ;QACd,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,OAAO,GAAG,IAAI,SAAS,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;YAE/C,IAAI,CAAC,OAAO,CAAC,OAAO,GAAG,CAAC,KAAK,EAAE,EAAE;;gBAC/B,MAAM,KAAK,GACT,OAAO,IAAI,KAAK;oBACd,CAAC,CAAE,KAAK,CAAC,KAAe;oBACxB,CAAC,CAAC,IAAI,KAAK,CAAC,oBAAoB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBAC7D,MAAM,CAAC,KAAK,CAAC,CAAC;gBACd,MAAA,IAAI,CAAC,OAAO,qDAAG,KAAK,CAAC,CAAC;YACxB,CAAC,CAAC;YAEF,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,GAAG,EAAE;gBACzB,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC;YAEF,IAAI,CAAC,OAAO,CAAC,OAAO,GAAG,GAAG,EAAE;;gBAC1B,MAAA,IAAI,CAAC,OAAO,oDAAI,CAAC;YACnB,CAAC,CAAC;YAEF,IAAI,CAAC,OAAO,CAAC,SAAS,GAAG,CAAC,KAAmB,EAAE,EAAE;;gBAC/C,IAAI,OAAuB,CAAC;gBAC5B,IAAI,CAAC;oBACH,OAAO,GAAG,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC/D,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAA,IAAI,CAAC,OAAO,qDAAG,KAAc,CAAC,CAAC;oBAC/B,OAAO;gBACT,CAAC;gBAED,MAAA,IAAI,CAAC,SAAS,qDAAG,OAAO,CAAC,CAAC;YAC5B,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,KAAK;;QACT,MAAA,IAAI,CAAC,OAAO,0CAAE,KAAK,EAAE,CAAC;IACxB,CAAC;IAED,IAAI,CAAC,OAAuB;QAC1B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;;YACrC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;gBAClB,MAAM,CAAC,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;gBACnC,OAAO;YACT,CAAC;YAED,MAAA,IAAI,CAAC,OAAO,0CAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;YAC5C,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}

30
packages/mcp-typescript/dist/server/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,30 @@
import { Protocol } from "../shared/protocol.js";
import { ClientCapabilities, Implementation, ServerNotification, ServerRequest, ServerResult } from "../types.js";
/**
* An MCP server on top of a pluggable transport.
*
* This server will automatically respond to the initialization flow as initiated from the client.
*/
export declare class Server extends Protocol<ServerRequest, ServerNotification, ServerResult> {
private _serverInfo;
private _clientCapabilities?;
private _clientVersion?;
/**
* Callback for when initialization has fully completed (i.e., the client has sent an `initialized` notification).
*/
oninitialized?: () => void;
/**
* Initializes this server with the given name and version information.
*/
constructor(_serverInfo: Implementation);
private _oninitialize;
/**
* After initialization has completed, this will be populated with the client's reported capabilities.
*/
getClientCapabilities(): ClientCapabilities | undefined;
/**
* After initialization has completed, this will be populated with information about the client's name and version.
*/
getClientVersion(): Implementation | undefined;
}
//# sourceMappingURL=index.d.ts.map

1
packages/mcp-typescript/dist/server/index.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EACL,kBAAkB,EAClB,cAAc,EAMd,kBAAkB,EAClB,aAAa,EACb,YAAY,EACb,MAAM,aAAa,CAAC;AAErB;;;;GAIG;AACH,qBAAa,MAAO,SAAQ,QAAQ,CAClC,aAAa,EACb,kBAAkB,EAClB,YAAY,CACb;IAYa,OAAO,CAAC,WAAW;IAX/B,OAAO,CAAC,mBAAmB,CAAC,CAAqB;IACjD,OAAO,CAAC,cAAc,CAAC,CAAiB;IAExC;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAE3B;;OAEG;gBACiB,WAAW,EAAE,cAAc;YAWjC,aAAa;IAmB3B;;OAEG;IACH,qBAAqB,IAAI,kBAAkB,GAAG,SAAS;IAIvD;;OAEG;IACH,gBAAgB,IAAI,cAAc,GAAG,SAAS;CAG/C"}

43
packages/mcp-typescript/dist/server/index.js generated vendored Normal file
View File

@@ -0,0 +1,43 @@
import { Protocol } from "../shared/protocol.js";
import { InitializedNotificationSchema, InitializeRequestSchema, PROTOCOL_VERSION, } from "../types.js";
/**
* An MCP server on top of a pluggable transport.
*
* This server will automatically respond to the initialization flow as initiated from the client.
*/
export class Server extends Protocol {
/**
* Initializes this server with the given name and version information.
*/
constructor(_serverInfo) {
super();
this._serverInfo = _serverInfo;
this.setRequestHandler(InitializeRequestSchema, (request) => this._oninitialize(request));
this.setNotificationHandler(InitializedNotificationSchema, () => { var _a; return (_a = this.oninitialized) === null || _a === void 0 ? void 0 : _a.call(this); });
}
async _oninitialize(request) {
if (request.params.protocolVersion !== PROTOCOL_VERSION) {
throw new Error(`Client's protocol version is not supported: ${request.params.protocolVersion}`);
}
this._clientCapabilities = request.params.capabilities;
this._clientVersion = request.params.clientInfo;
return {
protocolVersion: PROTOCOL_VERSION,
capabilities: {},
serverInfo: this._serverInfo,
};
}
/**
* After initialization has completed, this will be populated with the client's reported capabilities.
*/
getClientCapabilities() {
return this._clientCapabilities;
}
/**
* After initialization has completed, this will be populated with information about the client's name and version.
*/
getClientVersion() {
return this._clientVersion;
}
}
//# sourceMappingURL=index.js.map

1
packages/mcp-typescript/dist/server/index.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAGL,6BAA6B,EAE7B,uBAAuB,EAEvB,gBAAgB,GAIjB,MAAM,aAAa,CAAC;AAErB;;;;GAIG;AACH,MAAM,OAAO,MAAO,SAAQ,QAI3B;IASC;;OAEG;IACH,YAAoB,WAA2B;QAC7C,KAAK,EAAE,CAAC;QADU,gBAAW,GAAX,WAAW,CAAgB;QAG7C,IAAI,CAAC,iBAAiB,CAAC,uBAAuB,EAAE,CAAC,OAAO,EAAE,EAAE,CAC1D,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAC5B,CAAC;QACF,IAAI,CAAC,sBAAsB,CAAC,6BAA6B,EAAE,GAAG,EAAE,WAC9D,OAAA,MAAA,IAAI,CAAC,aAAa,oDAAI,CAAA,EAAA,CACvB,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,aAAa,CACzB,OAA0B;QAE1B,IAAI,OAAO,CAAC,MAAM,CAAC,eAAe,KAAK,gBAAgB,EAAE,CAAC;YACxD,MAAM,IAAI,KAAK,CACb,+CAA+C,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,CAChF,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC;QACvD,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC;QAEhD,OAAO;YACL,eAAe,EAAE,gBAAgB;YACjC,YAAY,EAAE,EAAE;YAChB,UAAU,EAAE,IAAI,CAAC,WAAW;SAC7B,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,qBAAqB;QACnB,OAAO,IAAI,CAAC,mBAAmB,CAAC;IAClC,CAAC;IAED;;OAEG;IACH,gBAAgB;QACd,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;CACF"}

45
packages/mcp-typescript/dist/server/sse.d.ts generated vendored Normal file
View File

@@ -0,0 +1,45 @@
import { IncomingMessage, ServerResponse } from "node:http";
import { Transport } from "../shared/transport.js";
import { JSONRPCMessage } from "../types.js";
/**
* Server transport for SSE: this will send messages over an SSE connection and receive messages from HTTP POST requests.
*
* This transport is only available in Node.js environments.
*/
export declare class SSEServerTransport implements Transport {
private _endpoint;
private _sseResponse?;
private _sessionId;
onclose?: () => void;
onerror?: (error: Error) => void;
onmessage?: (message: JSONRPCMessage) => void;
/**
* Creates a new SSE server transport, which will direct the client to POST messages to the relative or absolute URL identified by `_endpoint`.
*/
constructor(_endpoint: string);
/**
* Handles the initial SSE connection request.
*
* This should be called when a GET request is made to establish the SSE stream.
*/
connectSSE(req: IncomingMessage, res: ServerResponse): Promise<void>;
/**
* Handles incoming POST messages.
*
* This should be called when a POST request is made to send a message to the server.
*/
handlePostMessage(req: IncomingMessage, res: ServerResponse): Promise<void>;
/**
* Handle a client message, regardless of how it arrived. This can be used to inform the server of messages that arrive via a means different than HTTP POST.
*/
handleMessage(message: unknown): Promise<void>;
close(): Promise<void>;
send(message: JSONRPCMessage): Promise<void>;
/**
* Returns the session ID for this transport.
*
* This can be used to route incoming POST requests.
*/
get sessionId(): string;
}
//# sourceMappingURL=sse.d.ts.map

1
packages/mcp-typescript/dist/server/sse.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/server/sse.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAwB,MAAM,aAAa,CAAC;AAMnE;;;;GAIG;AACH,qBAAa,kBAAmB,YAAW,SAAS;IAWtC,OAAO,CAAC,SAAS;IAV7B,OAAO,CAAC,YAAY,CAAC,CAAiB;IACtC,OAAO,CAAC,UAAU,CAAS;IAE3B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAE9C;;OAEG;gBACiB,SAAS,EAAE,MAAM;IAIrC;;;;OAIG;IACG,UAAU,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB1E;;;;OAIG;IACG,iBAAiB,CACrB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,IAAI,CAAC;IAkChB;;OAEG;IACG,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAY9C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAMtB,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAUlD;;;;OAIG;IACH,IAAI,SAAS,IAAI,MAAM,CAEtB;CACF"}

115
packages/mcp-typescript/dist/server/sse.js generated vendored Normal file
View File

@@ -0,0 +1,115 @@
import { randomUUID } from "node:crypto";
import { JSONRPCMessageSchema } from "../types.js";
import getRawBody from "raw-body";
import contentType from "content-type";
const MAXIMUM_MESSAGE_SIZE = "4mb";
/**
* Server transport for SSE: this will send messages over an SSE connection and receive messages from HTTP POST requests.
*
* This transport is only available in Node.js environments.
*/
export class SSEServerTransport {
/**
* Creates a new SSE server transport, which will direct the client to POST messages to the relative or absolute URL identified by `_endpoint`.
*/
constructor(_endpoint) {
this._endpoint = _endpoint;
this._sessionId = randomUUID();
}
/**
* Handles the initial SSE connection request.
*
* This should be called when a GET request is made to establish the SSE stream.
*/
async connectSSE(req, res) {
if (this._sseResponse) {
throw new Error("Already connected!");
}
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
});
// Send the endpoint event
res.write(`event: endpoint\ndata: ${encodeURI(this._endpoint)}?sessionId=${this._sessionId}\n\n`);
this._sseResponse = res;
res.on("close", () => {
var _a;
this._sseResponse = undefined;
(_a = this.onclose) === null || _a === void 0 ? void 0 : _a.call(this);
});
}
/**
* Handles incoming POST messages.
*
* This should be called when a POST request is made to send a message to the server.
*/
async handlePostMessage(req, res) {
var _a, _b, _c;
if (!this._sseResponse) {
const message = "SSE connection not established";
res.writeHead(500).end(message);
throw new Error(message);
}
let body;
try {
const ct = contentType.parse((_a = req.headers["content-type"]) !== null && _a !== void 0 ? _a : "");
if (ct.type !== "application/json") {
throw new Error(`Unsupported content-type: ${ct}`);
}
body = await getRawBody(req, {
limit: MAXIMUM_MESSAGE_SIZE,
encoding: (_b = ct.parameters.charset) !== null && _b !== void 0 ? _b : "utf-8",
});
}
catch (error) {
res.writeHead(400).end(String(error));
(_c = this.onerror) === null || _c === void 0 ? void 0 : _c.call(this, error);
return;
}
try {
await this.handleMessage(JSON.parse(body));
}
catch (_d) {
res.writeHead(400).end(`Invalid message: ${body}`);
return;
}
res.writeHead(202).end("Accepted");
}
/**
* Handle a client message, regardless of how it arrived. This can be used to inform the server of messages that arrive via a means different than HTTP POST.
*/
async handleMessage(message) {
var _a, _b;
let parsedMessage;
try {
parsedMessage = JSONRPCMessageSchema.parse(message);
}
catch (error) {
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
throw error;
}
(_b = this.onmessage) === null || _b === void 0 ? void 0 : _b.call(this, parsedMessage);
}
async close() {
var _a, _b;
(_a = this._sseResponse) === null || _a === void 0 ? void 0 : _a.end();
this._sseResponse = undefined;
(_b = this.onclose) === null || _b === void 0 ? void 0 : _b.call(this);
}
async send(message) {
if (!this._sseResponse) {
throw new Error("Not connected");
}
this._sseResponse.write(`event: message\ndata: ${JSON.stringify(message)}\n\n`);
}
/**
* Returns the session ID for this transport.
*
* This can be used to route incoming POST requests.
*/
get sessionId() {
return this._sessionId;
}
}
//# sourceMappingURL=sse.js.map

1
packages/mcp-typescript/dist/server/sse.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"sse.js","sourceRoot":"","sources":["../../src/server/sse.ts"],"names":[],"mappings":"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"}

27
packages/mcp-typescript/dist/server/stdio.d.ts generated vendored Normal file
View File

@@ -0,0 +1,27 @@
import { Readable, Writable } from "node:stream";
import { JSONRPCMessage } from "../types.js";
import { Transport } from "../shared/transport.js";
/**
* Server transport for stdio: this communicates with a MCP client by reading from the current process' stdin and writing to stdout.
*
* This transport is only available in Node.js environments.
*/
export declare class StdioServerTransport implements Transport {
private _stdin;
private _stdout;
private _readBuffer;
constructor(_stdin?: Readable, _stdout?: Writable);
onclose?: () => void;
onerror?: (error: Error) => void;
onmessage?: (message: JSONRPCMessage) => void;
_ondata: (chunk: Buffer) => void;
_onerror: (error: Error) => void;
/**
* Starts listening for messages on stdin.
*/
start(): Promise<void>;
private processReadBuffer;
close(): Promise<void>;
send(message: JSONRPCMessage): Promise<void>;
}
//# sourceMappingURL=stdio.d.ts.map

1
packages/mcp-typescript/dist/server/stdio.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../src/server/stdio.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEnD;;;;GAIG;AACH,qBAAa,oBAAqB,YAAW,SAAS;IAIlD,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,OAAO;IAJjB,OAAO,CAAC,WAAW,CAAgC;gBAGzC,MAAM,GAAE,QAAwB,EAChC,OAAO,GAAE,QAAyB;IAG5C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAG9C,OAAO,UAAW,MAAM,UAGtB;IACF,QAAQ,UAAW,KAAK,UAEtB;IAEF;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B,OAAO,CAAC,iBAAiB;IAenB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;CAU7C"}

64
packages/mcp-typescript/dist/server/stdio.js generated vendored Normal file
View File

@@ -0,0 +1,64 @@
import process from "node:process";
import { ReadBuffer, serializeMessage } from "../shared/stdio.js";
/**
* Server transport for stdio: this communicates with a MCP client by reading from the current process' stdin and writing to stdout.
*
* This transport is only available in Node.js environments.
*/
export class StdioServerTransport {
constructor(_stdin = process.stdin, _stdout = process.stdout) {
this._stdin = _stdin;
this._stdout = _stdout;
this._readBuffer = new ReadBuffer();
// Arrow functions to bind `this` properly, while maintaining function identity.
this._ondata = (chunk) => {
this._readBuffer.append(chunk);
this.processReadBuffer();
};
this._onerror = (error) => {
var _a;
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
};
}
/**
* Starts listening for messages on stdin.
*/
async start() {
this._stdin.on("data", this._ondata);
this._stdin.on("error", this._onerror);
}
processReadBuffer() {
var _a, _b;
while (true) {
try {
const message = this._readBuffer.readMessage();
if (message === null) {
break;
}
(_a = this.onmessage) === null || _a === void 0 ? void 0 : _a.call(this, message);
}
catch (error) {
(_b = this.onerror) === null || _b === void 0 ? void 0 : _b.call(this, error);
}
}
}
async close() {
var _a;
this._stdin.off("data", this._ondata);
this._stdin.off("error", this._onerror);
this._readBuffer.clear();
(_a = this.onclose) === null || _a === void 0 ? void 0 : _a.call(this);
}
send(message) {
return new Promise((resolve) => {
const json = serializeMessage(message);
if (this._stdout.write(json)) {
resolve();
}
else {
this._stdout.once("drain", resolve);
}
});
}
}
//# sourceMappingURL=stdio.js.map

1
packages/mcp-typescript/dist/server/stdio.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/server/stdio.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,cAAc,CAAC;AAEnC,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAIlE;;;;GAIG;AACH,MAAM,OAAO,oBAAoB;IAG/B,YACU,SAAmB,OAAO,CAAC,KAAK,EAChC,UAAoB,OAAO,CAAC,MAAM;QADlC,WAAM,GAAN,MAAM,CAA0B;QAChC,YAAO,GAAP,OAAO,CAA2B;QAJpC,gBAAW,GAAe,IAAI,UAAU,EAAE,CAAC;QAWnD,gFAAgF;QAChF,YAAO,GAAG,CAAC,KAAa,EAAE,EAAE;YAC1B,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC/B,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3B,CAAC,CAAC;QACF,aAAQ,GAAG,CAAC,KAAY,EAAE,EAAE;;YAC1B,MAAA,IAAI,CAAC,OAAO,qDAAG,KAAK,CAAC,CAAC;QACxB,CAAC,CAAC;IAbC,CAAC;IAeJ;;OAEG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC;IAEO,iBAAiB;;QACvB,OAAO,IAAI,EAAE,CAAC;YACZ,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC;gBAC/C,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;oBACrB,MAAM;gBACR,CAAC;gBAED,MAAA,IAAI,CAAC,SAAS,qDAAG,OAAO,CAAC,CAAC;YAC5B,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAA,IAAI,CAAC,OAAO,qDAAG,KAAc,CAAC,CAAC;YACjC,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK;;QACT,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACtC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxC,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;QACzB,MAAA,IAAI,CAAC,OAAO,oDAAI,CAAC;IACnB,CAAC;IAED,IAAI,CAAC,OAAuB;QAC1B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,MAAM,IAAI,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;YACvC,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC7B,OAAO,EAAE,CAAC;YACZ,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACtC,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}

2
packages/mcp-typescript/dist/server/stdio.test.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=stdio.test.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"stdio.test.d.ts","sourceRoot":"","sources":["../../src/server/stdio.test.ts"],"names":[],"mappings":""}

87
packages/mcp-typescript/dist/server/stdio.test.js generated vendored Normal file
View File

@@ -0,0 +1,87 @@
import { Readable, Writable } from "node:stream";
import { ReadBuffer, serializeMessage } from "../shared/stdio.js";
import { StdioServerTransport } from "./stdio.js";
let input;
let outputBuffer;
let output;
beforeEach(() => {
input = new Readable({
// We'll use input.push() instead.
read: () => { },
});
outputBuffer = new ReadBuffer();
output = new Writable({
write(chunk, encoding, callback) {
outputBuffer.append(chunk);
callback();
},
});
});
test("should start then close cleanly", async () => {
const server = new StdioServerTransport(input, output);
server.onerror = (error) => {
throw error;
};
let didClose = false;
server.onclose = () => {
didClose = true;
};
await server.start();
expect(didClose).toBeFalsy();
await server.close();
expect(didClose).toBeTruthy();
});
test("should not read until started", async () => {
const server = new StdioServerTransport(input, output);
server.onerror = (error) => {
throw error;
};
let didRead = false;
const readMessage = new Promise((resolve) => {
server.onmessage = (message) => {
didRead = true;
resolve(message);
};
});
const message = {
jsonrpc: "2.0",
id: 1,
method: "ping",
};
input.push(serializeMessage(message));
expect(didRead).toBeFalsy();
await server.start();
expect(await readMessage).toEqual(message);
});
test("should read multiple messages", async () => {
const server = new StdioServerTransport(input, output);
server.onerror = (error) => {
throw error;
};
const messages = [
{
jsonrpc: "2.0",
id: 1,
method: "ping",
},
{
jsonrpc: "2.0",
method: "notifications/initialized",
},
];
const readMessages = [];
const finished = new Promise((resolve) => {
server.onmessage = (message) => {
readMessages.push(message);
if (JSON.stringify(message) === JSON.stringify(messages[1])) {
resolve();
}
};
});
input.push(serializeMessage(messages[0]));
input.push(serializeMessage(messages[1]));
await server.start();
await finished;
expect(readMessages).toEqual(messages);
});
//# sourceMappingURL=stdio.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"stdio.test.js","sourceRoot":"","sources":["../../src/server/stdio.test.ts"],"names":[],"mappings":"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"}

92
packages/mcp-typescript/dist/shared/protocol.d.ts generated vendored Normal file
View File

@@ -0,0 +1,92 @@
import { AnyZodObject, ZodLiteral, ZodObject, z } from "zod";
import { Notification, Progress, Request, Result } from "../types.js";
import { Transport } from "./transport.js";
/**
* Callback for progress notifications.
*/
export type ProgressCallback = (progress: Progress) => void;
/**
* Implements MCP protocol framing on top of a pluggable transport, including
* features like request/response linking, notifications, and progress.
*/
export declare class Protocol<SendRequestT extends Request, SendNotificationT extends Notification, SendResultT extends Result> {
private _transport?;
private _requestMessageId;
private _requestHandlers;
private _notificationHandlers;
private _responseHandlers;
private _progressHandlers;
/**
* Callback for when the connection is closed for any reason.
*
* This is invoked when close() is called as well.
*/
onclose?: () => void;
/**
* Callback for when an error occurs.
*
* Note that errors are not necessarily fatal; they are used for reporting any kind of exceptional condition out of band.
*/
onerror?: (error: Error) => void;
/**
* A handler to invoke for any request types that do not have their own handler installed.
*/
fallbackRequestHandler?: (request: Request) => Promise<SendResultT>;
/**
* A handler to invoke for any notification types that do not have their own handler installed.
*/
fallbackNotificationHandler?: (notification: Notification) => Promise<void>;
constructor();
/**
* Attaches to the given transport and starts listening for messages.
*
* The Protocol object assumes ownership of the Transport, replacing any callbacks that have already been set, and expects that it is the only user of the Transport instance going forward.
*/
connect(transport: Transport): Promise<void>;
private _onclose;
private _onerror;
private _onnotification;
private _onrequest;
private _onprogress;
private _onresponse;
get transport(): Transport | undefined;
/**
* Closes the connection.
*/
close(): Promise<void>;
/**
* Sends a request and wait for a response, with optional progress notifications in the meantime (if supported by the server).
*
* Do not use this method to emit notifications! Use notification() instead.
*/
request<T extends AnyZodObject>(request: SendRequestT, resultSchema: T, onprogress?: ProgressCallback): Promise<z.infer<T>>;
/**
* Emits a notification, which is a one-way message that does not expect a response.
*/
notification(notification: SendNotificationT): Promise<void>;
/**
* Registers a handler to invoke when this protocol object receives a request with the given method.
*
* Note that this will replace any previous request handler for the same method.
*/
setRequestHandler<T extends ZodObject<{
method: ZodLiteral<string>;
}>>(requestSchema: T, handler: (request: z.infer<T>) => SendResultT | Promise<SendResultT>): void;
/**
* Removes the request handler for the given method.
*/
removeRequestHandler(method: string): void;
/**
* Registers a handler to invoke when this protocol object receives a notification with the given method.
*
* Note that this will replace any previous notification handler for the same method.
*/
setNotificationHandler<T extends ZodObject<{
method: ZodLiteral<string>;
}>>(notificationSchema: T, handler: (notification: z.infer<T>) => void | Promise<void>): void;
/**
* Removes the notification handler for the given method.
*/
removeNotificationHandler(method: string): void;
}
//# sourceMappingURL=protocol.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/shared/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7D,OAAO,EAOL,YAAY,EAEZ,QAAQ,EAGR,OAAO,EACP,MAAM,EACP,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;AAE5D;;;GAGG;AACH,qBAAa,QAAQ,CACnB,YAAY,SAAS,OAAO,EAC5B,iBAAiB,SAAS,YAAY,EACtC,WAAW,SAAS,MAAM;IAE1B,OAAO,CAAC,UAAU,CAAC,CAAY;IAC/B,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,gBAAgB,CAGV;IACd,OAAO,CAAC,qBAAqB,CAGf;IACd,OAAO,CAAC,iBAAiB,CAGX;IACd,OAAO,CAAC,iBAAiB,CAA4C;IAErE;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;OAEG;IACH,sBAAsB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IAEpE;;OAEG;IACH,2BAA2B,CAAC,EAAE,CAAC,YAAY,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;;IAc5E;;;;OAIG;IACG,OAAO,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBlD,OAAO,CAAC,QAAQ;IAahB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,UAAU;IAiDlB,OAAO,CAAC,WAAW;IAenB,OAAO,CAAC,WAAW;IA0BnB,IAAI,SAAS,IAAI,SAAS,GAAG,SAAS,CAErC;IAED;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;;;OAIG;IACH,OAAO,CAAC,CAAC,SAAS,YAAY,EAC5B,OAAO,EAAE,YAAY,EACrB,YAAY,EAAE,CAAC,EACf,UAAU,CAAC,EAAE,gBAAgB,GAC5B,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAuCtB;;OAEG;IACG,YAAY,CAAC,YAAY,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAalE;;;;OAIG;IACH,iBAAiB,CACf,CAAC,SAAS,SAAS,CAAC;QAClB,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;KAC5B,CAAC,EAEF,aAAa,EAAE,CAAC,EAChB,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,GACnE,IAAI;IAMP;;OAEG;IACH,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI1C;;;;OAIG;IACH,sBAAsB,CACpB,CAAC,SAAS,SAAS,CAAC;QAClB,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;KAC5B,CAAC,EAEF,kBAAkB,EAAE,CAAC,EACrB,OAAO,EAAE,CAAC,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAC1D,IAAI;IAQP;;OAEG;IACH,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CAGhD"}

224
packages/mcp-typescript/dist/shared/protocol.js generated vendored Normal file
View File

@@ -0,0 +1,224 @@
import { ErrorCode, McpError, PingRequestSchema, ProgressNotificationSchema, } from "../types.js";
/**
* Implements MCP protocol framing on top of a pluggable transport, including
* features like request/response linking, notifications, and progress.
*/
export class Protocol {
constructor() {
this._requestMessageId = 0;
this._requestHandlers = new Map();
this._notificationHandlers = new Map();
this._responseHandlers = new Map();
this._progressHandlers = new Map();
this.setNotificationHandler(ProgressNotificationSchema, (notification) => {
this._onprogress(notification);
});
this.setRequestHandler(PingRequestSchema,
// Automatic pong by default.
(_request) => ({}));
}
/**
* Attaches to the given transport and starts listening for messages.
*
* The Protocol object assumes ownership of the Transport, replacing any callbacks that have already been set, and expects that it is the only user of the Transport instance going forward.
*/
async connect(transport) {
this._transport = transport;
this._transport.onclose = () => {
this._onclose();
};
this._transport.onerror = (error) => {
this._onerror(error);
};
this._transport.onmessage = (message) => {
if (!("method" in message)) {
this._onresponse(message);
}
else if ("id" in message) {
this._onrequest(message);
}
else {
this._onnotification(message);
}
};
}
_onclose() {
var _a;
const responseHandlers = this._responseHandlers;
this._responseHandlers = new Map();
this._progressHandlers.clear();
this._transport = undefined;
(_a = this.onclose) === null || _a === void 0 ? void 0 : _a.call(this);
const error = new McpError(ErrorCode.ConnectionClosed, "Connection closed");
for (const handler of responseHandlers.values()) {
handler(error);
}
}
_onerror(error) {
var _a;
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
}
_onnotification(notification) {
var _a;
const handler = (_a = this._notificationHandlers.get(notification.method)) !== null && _a !== void 0 ? _a : this.fallbackNotificationHandler;
// Ignore notifications not being subscribed to.
if (handler === undefined) {
return;
}
handler(notification).catch((error) => this._onerror(new Error(`Uncaught error in notification handler: ${error}`)));
}
_onrequest(request) {
var _a, _b;
const handler = (_a = this._requestHandlers.get(request.method)) !== null && _a !== void 0 ? _a : this.fallbackRequestHandler;
if (handler === undefined) {
(_b = this._transport) === null || _b === void 0 ? void 0 : _b.send({
jsonrpc: "2.0",
id: request.id,
error: {
code: ErrorCode.MethodNotFound,
message: "Method not found",
},
}).catch((error) => this._onerror(new Error(`Failed to send an error response: ${error}`)));
return;
}
handler(request)
.then((result) => {
var _a;
(_a = this._transport) === null || _a === void 0 ? void 0 : _a.send({
result,
jsonrpc: "2.0",
id: request.id,
});
}, (error) => {
var _a, _b;
return (_a = this._transport) === null || _a === void 0 ? void 0 : _a.send({
jsonrpc: "2.0",
id: request.id,
error: {
code: error["code"]
? Math.floor(Number(error["code"]))
: ErrorCode.InternalError,
message: (_b = error.message) !== null && _b !== void 0 ? _b : "Internal error",
},
});
})
.catch((error) => this._onerror(new Error(`Failed to send response: ${error}`)));
}
_onprogress(notification) {
const { progress, total, progressToken } = notification.params;
const handler = this._progressHandlers.get(Number(progressToken));
if (handler === undefined) {
this._onerror(new Error(`Received a progress notification for an unknown token: ${JSON.stringify(notification)}`));
return;
}
handler({ progress, total });
}
_onresponse(response) {
const messageId = response.id;
const handler = this._responseHandlers.get(Number(messageId));
if (handler === undefined) {
this._onerror(new Error(`Received a response for an unknown message ID: ${JSON.stringify(response)}`));
return;
}
this._responseHandlers.delete(Number(messageId));
this._progressHandlers.delete(Number(messageId));
if ("result" in response) {
handler(response);
}
else {
const error = new McpError(response.error.code, response.error.message, response.error.data);
handler(error);
}
}
get transport() {
return this._transport;
}
/**
* Closes the connection.
*/
async close() {
var _a;
await ((_a = this._transport) === null || _a === void 0 ? void 0 : _a.close());
}
/**
* Sends a request and wait for a response, with optional progress notifications in the meantime (if supported by the server).
*
* Do not use this method to emit notifications! Use notification() instead.
*/
request(request, resultSchema, onprogress) {
return new Promise((resolve, reject) => {
if (!this._transport) {
reject(new Error("Not connected"));
return;
}
const messageId = this._requestMessageId++;
const jsonrpcRequest = {
...request,
jsonrpc: "2.0",
id: messageId,
};
if (onprogress) {
this._progressHandlers.set(messageId, onprogress);
jsonrpcRequest.params = {
...request.params,
_meta: { progressToken: messageId },
};
}
this._responseHandlers.set(messageId, (response) => {
if (response instanceof Error) {
return reject(response);
}
try {
const result = resultSchema.parse(response.result);
resolve(result);
}
catch (error) {
reject(error);
}
});
this._transport.send(jsonrpcRequest).catch(reject);
});
}
/**
* Emits a notification, which is a one-way message that does not expect a response.
*/
async notification(notification) {
if (!this._transport) {
throw new Error("Not connected");
}
const jsonrpcNotification = {
...notification,
jsonrpc: "2.0",
};
await this._transport.send(jsonrpcNotification);
}
/**
* Registers a handler to invoke when this protocol object receives a request with the given method.
*
* Note that this will replace any previous request handler for the same method.
*/
setRequestHandler(requestSchema, handler) {
this._requestHandlers.set(requestSchema.shape.method.value, (request) => Promise.resolve(handler(requestSchema.parse(request))));
}
/**
* Removes the request handler for the given method.
*/
removeRequestHandler(method) {
this._requestHandlers.delete(method);
}
/**
* Registers a handler to invoke when this protocol object receives a notification with the given method.
*
* Note that this will replace any previous notification handler for the same method.
*/
setNotificationHandler(notificationSchema, handler) {
this._notificationHandlers.set(notificationSchema.shape.method.value, (notification) => Promise.resolve(handler(notificationSchema.parse(notification))));
}
/**
* Removes the notification handler for the given method.
*/
removeNotificationHandler(method) {
this._notificationHandlers.delete(method);
}
}
//# sourceMappingURL=protocol.js.map

1
packages/mcp-typescript/dist/shared/protocol.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

13
packages/mcp-typescript/dist/shared/stdio.d.ts generated vendored Normal file
View File

@@ -0,0 +1,13 @@
import { JSONRPCMessage } from "../types.js";
/**
* Buffers a continuous stdio stream into discrete JSON-RPC messages.
*/
export declare class ReadBuffer {
private _buffer?;
append(chunk: Buffer): void;
readMessage(): JSONRPCMessage | null;
clear(): void;
}
export declare function deserializeMessage(line: string): JSONRPCMessage;
export declare function serializeMessage(message: JSONRPCMessage): string;
//# sourceMappingURL=stdio.d.ts.map

1
packages/mcp-typescript/dist/shared/stdio.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../src/shared/stdio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAwB,MAAM,aAAa,CAAC;AAEnE;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAC,CAAS;IAEzB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI3B,WAAW,IAAI,cAAc,GAAG,IAAI;IAepC,KAAK,IAAI,IAAI;CAGd;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAE/D;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAEhE"}

31
packages/mcp-typescript/dist/shared/stdio.js generated vendored Normal file
View File

@@ -0,0 +1,31 @@
import { JSONRPCMessageSchema } from "../types.js";
/**
* Buffers a continuous stdio stream into discrete JSON-RPC messages.
*/
export class ReadBuffer {
append(chunk) {
this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
}
readMessage() {
if (!this._buffer) {
return null;
}
const index = this._buffer.indexOf("\n");
if (index === -1) {
return null;
}
const line = this._buffer.toString("utf8", 0, index);
this._buffer = this._buffer.subarray(index + 1);
return deserializeMessage(line);
}
clear() {
this._buffer = undefined;
}
}
export function deserializeMessage(line) {
return JSONRPCMessageSchema.parse(JSON.parse(line));
}
export function serializeMessage(message) {
return JSON.stringify(message) + "\n";
}
//# sourceMappingURL=stdio.js.map

1
packages/mcp-typescript/dist/shared/stdio.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/shared/stdio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEnE;;GAEG;AACH,MAAM,OAAO,UAAU;IAGrB,MAAM,CAAC,KAAa;QAClB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAC7E,CAAC;IAED,WAAW;QACT,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;QACrD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAChD,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;IAC3B,CAAC;CACF;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,OAAuB;IACtD,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;AACxC,CAAC"}

2
packages/mcp-typescript/dist/shared/stdio.test.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=stdio.test.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"stdio.test.d.ts","sourceRoot":"","sources":["../../src/shared/stdio.test.ts"],"names":[],"mappings":""}

27
packages/mcp-typescript/dist/shared/stdio.test.js generated vendored Normal file
View File

@@ -0,0 +1,27 @@
import { ReadBuffer } from "./stdio.js";
const testMessage = {
jsonrpc: "2.0",
method: "foobar",
};
test("should have no messages after initialization", () => {
const readBuffer = new ReadBuffer();
expect(readBuffer.readMessage()).toBeNull();
});
test("should only yield a message after a newline", () => {
const readBuffer = new ReadBuffer();
readBuffer.append(Buffer.from(JSON.stringify(testMessage)));
expect(readBuffer.readMessage()).toBeNull();
readBuffer.append(Buffer.from("\n"));
expect(readBuffer.readMessage()).toEqual(testMessage);
expect(readBuffer.readMessage()).toBeNull();
});
test("should be reusable after clearing", () => {
const readBuffer = new ReadBuffer();
readBuffer.append(Buffer.from("foobar"));
readBuffer.clear();
expect(readBuffer.readMessage()).toBeNull();
readBuffer.append(Buffer.from(JSON.stringify(testMessage)));
readBuffer.append(Buffer.from("\n"));
expect(readBuffer.readMessage()).toEqual(testMessage);
});
//# sourceMappingURL=stdio.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"stdio.test.js","sourceRoot":"","sources":["../../src/shared/stdio.test.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,WAAW,GAAmB;IAClC,OAAO,EAAE,KAAK;IACd,MAAM,EAAE,QAAQ;CACjB,CAAC;AAEF,IAAI,CAAC,8CAA8C,EAAE,GAAG,EAAE;IACxD,MAAM,UAAU,GAAG,IAAI,UAAU,EAAE,CAAC;IACpC,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;AAC9C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,6CAA6C,EAAE,GAAG,EAAE;IACvD,MAAM,UAAU,GAAG,IAAI,UAAU,EAAE,CAAC;IAEpC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAC5D,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;IAE5C,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACrC,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACtD,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;AAC9C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,mCAAmC,EAAE,GAAG,EAAE;IAC7C,MAAM,UAAU,GAAG,IAAI,UAAU,EAAE,CAAC;IAEpC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IACzC,UAAU,CAAC,KAAK,EAAE,CAAC;IACnB,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;IAE5C,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAC5D,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACrC,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;AACxD,CAAC,CAAC,CAAC"}

31
packages/mcp-typescript/dist/shared/transport.d.ts generated vendored Normal file
View File

@@ -0,0 +1,31 @@
import { JSONRPCMessage } from "../types.js";
/**
* Describes the minimal contract for a MCP transport that a client or server can communicate over.
*/
export interface Transport {
/**
* Sends a JSON-RPC message (request or response).
*/
send(message: JSONRPCMessage): Promise<void>;
/**
* Closes the connection.
*/
close(): Promise<void>;
/**
* Callback for when the connection is closed for any reason.
*
* This should be invoked when close() is called as well.
*/
onclose?: () => void;
/**
* Callback for when an error occurs.
*
* Note that errors are not necessarily fatal; they are used for reporting any kind of exceptional condition out of band.
*/
onerror?: (error: Error) => void;
/**
* Callback for when a message (request or response) is received over the connection.
*/
onmessage?: (message: JSONRPCMessage) => void;
}
//# sourceMappingURL=transport.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/shared/transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB;;OAEG;IACH,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7C;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;CAC/C"}

2
packages/mcp-typescript/dist/shared/transport.js generated vendored Normal file
View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=transport.js.map

1
packages/mcp-typescript/dist/shared/transport.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/shared/transport.ts"],"names":[],"mappings":""}

8082
packages/mcp-typescript/dist/types.d.ts generated vendored Normal file

File diff suppressed because it is too large Load Diff

1
packages/mcp-typescript/dist/types.d.ts.map generated vendored Normal file

File diff suppressed because one or more lines are too long

801
packages/mcp-typescript/dist/types.js generated vendored Normal file
View File

@@ -0,0 +1,801 @@
import { z } from "zod";
export const PROTOCOL_VERSION = "2024-10-07";
/* JSON-RPC types */
export const JSONRPC_VERSION = "2.0";
/**
* A progress token, used to associate progress notifications with the original request.
*/
export const ProgressTokenSchema = z.union([z.string(), z.number().int()]);
/**
* An opaque token used to represent a cursor for pagination.
*/
export const CursorSchema = z.string();
export const RequestSchema = z.object({
method: z.string(),
params: z.optional(z
.object({
_meta: z.optional(z
.object({
/**
* If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.
*/
progressToken: z.optional(ProgressTokenSchema),
})
.passthrough()),
})
.passthrough()),
});
export const NotificationSchema = z.object({
method: z.string(),
params: z.optional(z
.object({
/**
* This parameter name is reserved by MCP to allow clients and servers to attach additional metadata to their notifications.
*/
_meta: z.optional(z.object({}).passthrough()),
})
.passthrough()),
});
export const ResultSchema = z
.object({
/**
* This result property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses.
*/
_meta: z.optional(z.object({}).passthrough()),
})
.passthrough();
/**
* A uniquely identifying ID for a request in JSON-RPC.
*/
export const RequestIdSchema = z.union([z.string(), z.number().int()]);
/**
* A request that expects a response.
*/
export const JSONRPCRequestSchema = z
.object({
jsonrpc: z.literal(JSONRPC_VERSION),
id: RequestIdSchema,
})
.merge(RequestSchema)
.strict();
/**
* A notification which does not expect a response.
*/
export const JSONRPCNotificationSchema = z
.object({
jsonrpc: z.literal(JSONRPC_VERSION),
})
.merge(NotificationSchema)
.strict();
/**
* A successful (non-error) response to a request.
*/
export const JSONRPCResponseSchema = z
.object({
jsonrpc: z.literal(JSONRPC_VERSION),
id: RequestIdSchema,
result: ResultSchema,
})
.strict();
/**
* An incomplete set of error codes that may appear in JSON-RPC responses.
*/
export var ErrorCode;
(function (ErrorCode) {
// SDK error codes
ErrorCode[ErrorCode["ConnectionClosed"] = -1] = "ConnectionClosed";
// Standard JSON-RPC error codes
ErrorCode[ErrorCode["ParseError"] = -32700] = "ParseError";
ErrorCode[ErrorCode["InvalidRequest"] = -32600] = "InvalidRequest";
ErrorCode[ErrorCode["MethodNotFound"] = -32601] = "MethodNotFound";
ErrorCode[ErrorCode["InvalidParams"] = -32602] = "InvalidParams";
ErrorCode[ErrorCode["InternalError"] = -32603] = "InternalError";
})(ErrorCode || (ErrorCode = {}));
/**
* A response to a request that indicates an error occurred.
*/
export const JSONRPCErrorSchema = z
.object({
jsonrpc: z.literal(JSONRPC_VERSION),
id: RequestIdSchema,
error: z.object({
/**
* The error type that occurred.
*/
code: z.number().int(),
/**
* A short description of the error. The message SHOULD be limited to a concise single sentence.
*/
message: z.string(),
/**
* Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.).
*/
data: z.optional(z.unknown()),
}),
})
.strict();
export const JSONRPCMessageSchema = z.union([
JSONRPCRequestSchema,
JSONRPCNotificationSchema,
JSONRPCResponseSchema,
JSONRPCErrorSchema,
]);
/* Empty result */
/**
* A response that indicates success but carries no data.
*/
export const EmptyResultSchema = ResultSchema.strict();
/* Initialization */
/**
* Text provided to or from an LLM.
*/
export const TextContentSchema = z.object({
type: z.literal("text"),
/**
* The text content of the message.
*/
text: z.string(),
});
/**
* An image provided to or from an LLM.
*/
export const ImageContentSchema = z.object({
type: z.literal("image"),
/**
* The base64-encoded image data.
*/
data: z.string().base64(),
/**
* The MIME type of the image. Different providers may support different image types.
*/
mimeType: z.string(),
});
/**
* Describes a message issued to or received from an LLM API.
*/
export const SamplingMessageSchema = z.object({
role: z.enum(["user", "assistant"]),
content: z.union([TextContentSchema, ImageContentSchema]),
});
/**
* Describes the name and version of an MCP implementation.
*/
export const ImplementationSchema = z.object({
name: z.string(),
version: z.string(),
});
/**
* Capabilities a client may support. Known capabilities are defined here, in this schema, but this is not a closed set: any client can define its own, additional capabilities.
*/
export const ClientCapabilitiesSchema = z.object({
/**
* Experimental, non-standard capabilities that the client supports.
*/
experimental: z.optional(z.object({}).passthrough()),
/**
* Present if the client supports sampling from an LLM.
*/
sampling: z.optional(z.object({}).passthrough()),
});
/**
* This request is sent from the client to the server when it first connects, asking it to begin initialization.
*/
export const InitializeRequestSchema = RequestSchema.extend({
method: z.literal("initialize"),
params: z.object({
/**
* The latest version of the Model Context Protocol that the client supports. The client MAY decide to support older versions as well.
*/
protocolVersion: z.string().or(z.number().int()),
capabilities: ClientCapabilitiesSchema,
clientInfo: ImplementationSchema,
}),
});
/**
* Capabilities that a server may support. Known capabilities are defined here, in this schema, but this is not a closed set: any server can define its own, additional capabilities.
*/
export const ServerCapabilitiesSchema = z.object({
/**
* Experimental, non-standard capabilities that the server supports.
*/
experimental: z.optional(z.object({}).passthrough()),
/**
* Present if the server supports sending log messages to the client.
*/
logging: z.optional(z.object({}).passthrough()),
/**
* Present if the server offers any prompt templates.
*/
prompts: z.optional(z
.object({
/**
* Whether this server supports notifications for changes to the prompt list.
*/
listChanged: z.optional(z.boolean()),
})
.passthrough()),
/**
* Present if the server offers any resources to read.
*/
resources: z.optional(z
.object({
/**
* Whether this server supports subscribing to resource updates.
*/
subscribe: z.optional(z.boolean()),
/**
* Whether this server supports notifications for changes to the resource list.
*/
listChanged: z.optional(z.boolean()),
})
.passthrough()),
/**
* Present if the server offers any tools to call.
*/
tools: z.optional(z
.object({
/**
* Whether this server supports notifications for changes to the tool list.
*/
listChanged: z.optional(z.boolean()),
})
.passthrough()),
});
/**
* After receiving an initialize request from the client, the server sends this response.
*/
export const InitializeResultSchema = ResultSchema.extend({
/**
* The version of the Model Context Protocol that the server wants to use. This may not match the version that the client requested. If the client cannot support this version, it MUST disconnect.
*/
protocolVersion: z.string().or(z.number().int()),
capabilities: ServerCapabilitiesSchema,
serverInfo: ImplementationSchema,
});
/**
* This notification is sent from the client to the server after initialization has finished.
*/
export const InitializedNotificationSchema = NotificationSchema.extend({
method: z.literal("notifications/initialized"),
});
/* Ping */
/**
* A ping, issued by either the server or the client, to check that the other party is still alive. The receiver must promptly respond, or else may be disconnected.
*/
export const PingRequestSchema = RequestSchema.extend({
method: z.literal("ping"),
});
/* Progress notifications */
export const ProgressSchema = z.object({
/**
* The progress thus far. This should increase every time progress is made, even if the total is unknown.
*/
progress: z.number(),
/**
* Total number of items to process (or total progress required), if known.
*/
total: z.optional(z.number()),
});
/**
* An out-of-band notification used to inform the receiver of a progress update for a long-running request.
*/
export const ProgressNotificationSchema = NotificationSchema.extend({
method: z.literal("notifications/progress"),
params: ProgressSchema.extend({
/**
* The progress token which was given in the initial request, used to associate this notification with the request that is proceeding.
*/
progressToken: ProgressTokenSchema,
}),
});
/* Pagination */
export const PaginatedRequestSchema = RequestSchema.extend({
params: z.optional(z.object({
/**
* An opaque token representing the current pagination position.
* If provided, the server should return results starting after this cursor.
*/
cursor: z.optional(CursorSchema),
})),
});
export const PaginatedResultSchema = ResultSchema.extend({
/**
* An opaque token representing the pagination position after the last returned result.
* If present, there may be more results available.
*/
nextCursor: z.optional(CursorSchema),
});
/* Resources */
/**
* The contents of a specific resource or sub-resource.
*/
export const ResourceContentsSchema = z.object({
/**
* The URI of this resource.
*/
uri: z.string().transform((s) => new URL(s)),
/**
* The MIME type of this resource, if known.
*/
mimeType: z.optional(z.string()),
});
export const TextResourceContentsSchema = ResourceContentsSchema.extend({
/**
* The text of the item. This must only be set if the item can actually be represented as text (not binary data).
*/
text: z.string(),
});
export const BlobResourceContentsSchema = ResourceContentsSchema.extend({
/**
* A base64-encoded string representing the binary data of the item.
*/
blob: z.string().base64(),
});
/**
* A known resource that the server is capable of reading.
*/
export const ResourceSchema = z.object({
/**
* The URI of this resource.
*/
uri: z.string().transform((s) => new URL(s)),
/**
* A human-readable name for this resource.
*
* This can be used by clients to populate UI elements.
*/
name: z.string(),
/**
* A description of what this resource represents.
*
* This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model.
*/
description: z.optional(z.string()),
/**
* The MIME type of this resource, if known.
*/
mimeType: z.optional(z.string()),
});
/**
* A template description for resources available on the server.
*/
export const ResourceTemplateSchema = z.object({
/**
* A URI template (according to RFC 6570) that can be used to construct resource URIs.
*/
uriTemplate: z.string(),
/**
* A human-readable name for the type of resource this template refers to.
*
* This can be used by clients to populate UI elements.
*/
name: z.string(),
/**
* A description of what this template is for.
*
* This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model.
*/
description: z.optional(z.string()),
/**
* The MIME type for all resources that match this template. This should only be included if all resources matching this template have the same type.
*/
mimeType: z.optional(z.string()),
});
/**
* Sent from the client to request a list of resources the server has.
*/
export const ListResourcesRequestSchema = PaginatedRequestSchema.extend({
method: z.literal("resources/list"),
});
/**
* The server's response to a resources/list request from the client.
*/
export const ListResourcesResultSchema = PaginatedResultSchema.extend({
resources: z.array(ResourceSchema),
});
/**
* Sent from the client to request a list of resource templates the server has.
*/
export const ListResourceTemplatesRequestSchema = PaginatedRequestSchema.extend({
method: z.literal("resources/templates/list"),
});
/**
* The server's response to a resources/templates/list request from the client.
*/
export const ListResourceTemplatesResultSchema = PaginatedResultSchema.extend({
resourceTemplates: z.array(ResourceTemplateSchema),
});
/**
* Sent from the client to the server, to read a specific resource URI.
*/
export const ReadResourceRequestSchema = RequestSchema.extend({
method: z.literal("resources/read"),
params: z.object({
/**
* The URI of the resource to read. The URI can use any protocol; it is up to the server how to interpret it.
*/
uri: z.string().transform((s) => new URL(s)),
}),
});
/**
* The server's response to a resources/read request from the client.
*/
export const ReadResourceResultSchema = ResultSchema.extend({
contents: z.array(z.union([TextResourceContentsSchema, BlobResourceContentsSchema])),
});
/**
* An optional notification from the server to the client, informing it that the list of resources it can read from has changed. This may be issued by servers without any previous subscription from the client.
*/
export const ResourceListChangedNotificationSchema = NotificationSchema.extend({
method: z.literal("notifications/resources/list_changed"),
});
/**
* Sent from the client to request resources/updated notifications from the server whenever a particular resource changes.
*/
export const SubscribeRequestSchema = RequestSchema.extend({
method: z.literal("resources/subscribe"),
params: z.object({
/**
* The URI of the resource to subscribe to. The URI can use any protocol; it is up to the server how to interpret it.
*/
uri: z.string().transform((s) => new URL(s)),
}),
});
/**
* Sent from the client to request cancellation of resources/updated notifications from the server. This should follow a previous resources/subscribe request.
*/
export const UnsubscribeRequestSchema = RequestSchema.extend({
method: z.literal("resources/unsubscribe"),
params: z.object({
/**
* The URI of the resource to unsubscribe from.
*/
uri: z.string().transform((s) => new URL(s)),
}),
});
/**
* A notification from the server to the client, informing it that a resource has changed and may need to be read again. This should only be sent if the client previously sent a resources/subscribe request.
*/
export const ResourceUpdatedNotificationSchema = NotificationSchema.extend({
method: z.literal("notifications/resources/updated"),
params: z.object({
/**
* The URI of the resource that has been updated. This might be a sub-resource of the one that the client actually subscribed to.
*/
uri: z.string().transform((s) => new URL(s)),
}),
});
/* Prompts */
/**
* Describes an argument that a prompt can accept.
*/
export const PromptArgumentSchema = z.object({
/**
* The name of the argument.
*/
name: z.string(),
/**
* A human-readable description of the argument.
*/
description: z.optional(z.string()),
/**
* Whether this argument must be provided.
*/
required: z.optional(z.boolean()),
});
/**
* A prompt or prompt template that the server offers.
*/
export const PromptSchema = z.object({
/**
* The name of the prompt or prompt template.
*/
name: z.string(),
/**
* An optional description of what this prompt provides
*/
description: z.optional(z.string()),
/**
* A list of arguments to use for templating the prompt.
*/
arguments: z.optional(z.array(PromptArgumentSchema)),
});
/**
* Sent from the client to request a list of prompts and prompt templates the server has.
*/
export const ListPromptsRequestSchema = PaginatedRequestSchema.extend({
method: z.literal("prompts/list"),
});
/**
* The server's response to a prompts/list request from the client.
*/
export const ListPromptsResultSchema = PaginatedResultSchema.extend({
prompts: z.array(PromptSchema),
});
/**
* Used by the client to get a prompt provided by the server.
*/
export const GetPromptRequestSchema = RequestSchema.extend({
method: z.literal("prompts/get"),
params: z.object({
/**
* The name of the prompt or prompt template.
*/
name: z.string(),
/**
* Arguments to use for templating the prompt.
*/
arguments: z.optional(z.record(z.string())),
}),
});
/**
* The server's response to a prompts/get request from the client.
*/
export const GetPromptResultSchema = ResultSchema.extend({
/**
* An optional description for the prompt.
*/
description: z.optional(z.string()),
messages: z.array(SamplingMessageSchema),
});
/**
* An optional notification from the server to the client, informing it that the list of prompts it offers has changed. This may be issued by servers without any previous subscription from the client.
*/
export const PromptListChangedNotificationSchema = NotificationSchema.extend({
method: z.literal("notifications/prompts/list_changed"),
});
/* Tools */
/**
* Definition for a tool the client can call.
*/
export const ToolSchema = z.object({
/**
* The name of the tool.
*/
name: z.string(),
/**
* A human-readable description of the tool.
*/
description: z.optional(z.string()),
/**
* A JSON Schema object defining the expected parameters for the tool.
*/
inputSchema: z.object({
type: z.literal("object"),
properties: z.optional(z.object({}).passthrough()),
}),
});
/**
* Sent from the client to request a list of tools the server has.
*/
export const ListToolsRequestSchema = PaginatedRequestSchema.extend({
method: z.literal("tools/list"),
});
/**
* The server's response to a tools/list request from the client.
*/
export const ListToolsResultSchema = PaginatedResultSchema.extend({
tools: z.array(ToolSchema),
});
/**
* The server's response to a tool call.
*/
export const CallToolResultSchema = ResultSchema.extend({
toolResult: z.unknown(),
});
/**
* Used by the client to invoke a tool provided by the server.
*/
export const CallToolRequestSchema = RequestSchema.extend({
method: z.literal("tools/call"),
params: z.object({
name: z.string(),
arguments: z.optional(z.record(z.unknown())),
}),
});
/**
* An optional notification from the server to the client, informing it that the list of tools it offers has changed. This may be issued by servers without any previous subscription from the client.
*/
export const ToolListChangedNotificationSchema = NotificationSchema.extend({
method: z.literal("notifications/tools/list_changed"),
});
/* Logging */
/**
* The severity of a log message.
*/
export const LoggingLevelSchema = z.enum(["debug", "info", "warning", "error"]);
/**
* A request from the client to the server, to enable or adjust logging.
*/
export const SetLevelRequestSchema = RequestSchema.extend({
method: z.literal("logging/setLevel"),
params: z.object({
/**
* The level of logging that the client wants to receive from the server. The server should send all logs at this level and higher (i.e., more severe) to the client as notifications/logging/message.
*/
level: LoggingLevelSchema,
}),
});
/**
* Notification of a log message passed from server to client. If no logging/setLevel request has been sent from the client, the server MAY decide which messages to send automatically.
*/
export const LoggingMessageNotificationSchema = NotificationSchema.extend({
method: z.literal("notifications/message"),
params: z.object({
/**
* The severity of this log message.
*/
level: LoggingLevelSchema,
/**
* An optional name of the logger issuing this message.
*/
logger: z.optional(z.string()),
/**
* The data to be logged, such as a string message or an object. Any JSON serializable type is allowed here.
*/
data: z.unknown(),
}),
});
/* Sampling */
/**
* A request from the server to sample an LLM via the client. The client has full discretion over which model to select. The client should also inform the user before beginning sampling, to allow them to inspect the request (human in the loop) and decide whether to approve it.
*/
export const CreateMessageRequestSchema = RequestSchema.extend({
method: z.literal("sampling/createMessage"),
params: z.object({
messages: z.array(SamplingMessageSchema),
/**
* An optional system prompt the server wants to use for sampling. The client MAY modify or omit this prompt.
*/
systemPrompt: z.optional(z.string()),
/**
* A request to include context from one or more MCP servers (including the caller), to be attached to the prompt. The client MAY ignore this request.
*/
includeContext: z.optional(z.enum(["none", "thisServer", "allServers"])),
temperature: z.optional(z.number()),
/**
* The maximum number of tokens to sample, as requested by the server. The client MAY choose to sample fewer tokens than requested.
*/
maxTokens: z.number().int(),
stopSequences: z.optional(z.array(z.string())),
/**
* Optional metadata to pass through to the LLM provider. The format of this metadata is provider-specific.
*/
metadata: z.optional(z.object({}).passthrough()),
}),
});
/**
* The client's response to a sampling/create_message request from the server. The client should inform the user before returning the sampled message, to allow them to inspect the response (human in the loop) and decide whether to allow the server to see it.
*/
export const CreateMessageResultSchema = ResultSchema.extend({
/**
* The name of the model that generated the message.
*/
model: z.string(),
/**
* The reason why sampling stopped.
*/
stopReason: z.enum(["endTurn", "stopSequence", "maxTokens"]),
role: z.enum(["user", "assistant"]),
content: z.discriminatedUnion("type", [
TextContentSchema,
ImageContentSchema,
]),
});
/* Autocomplete */
/**
* A reference to a resource or resource template definition.
*/
export const ResourceReferenceSchema = z.object({
type: z.literal("ref/resource"),
/**
* The URI or URI template of the resource.
*/
uri: z.string(),
});
/**
* Identifies a prompt.
*/
export const PromptReferenceSchema = z.object({
type: z.literal("ref/prompt"),
/**
* The name of the prompt or prompt template
*/
name: z.string(),
});
/**
* A request from the client to the server, to ask for completion options.
*/
export const CompleteRequestSchema = RequestSchema.extend({
method: z.literal("completion/complete"),
params: z.object({
ref: z.union([PromptReferenceSchema, ResourceReferenceSchema]),
/**
* The argument's information
*/
argument: z.object({
/**
* The name of the argument
*/
name: z.string(),
/**
* The value of the argument to use for completion matching.
*/
value: z.string(),
}),
}),
});
/**
* The server's response to a completion/complete request
*/
export const CompleteResultSchema = ResultSchema.extend({
completion: z.object({
/**
* An array of completion values. Must not exceed 100 items.
*/
values: z.array(z.string()).max(100),
/**
* The total number of completion options available. This can exceed the number of values actually sent in the response.
*/
total: z.optional(z.number().int()),
/**
* Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown.
*/
hasMore: z.optional(z.boolean()),
}),
});
/* Client messages */
export const ClientRequestSchema = z.union([
PingRequestSchema,
InitializeRequestSchema,
CompleteRequestSchema,
SetLevelRequestSchema,
GetPromptRequestSchema,
ListPromptsRequestSchema,
ListResourcesRequestSchema,
ReadResourceRequestSchema,
SubscribeRequestSchema,
UnsubscribeRequestSchema,
CallToolRequestSchema,
ListToolsRequestSchema,
]);
export const ClientNotificationSchema = z.union([
ProgressNotificationSchema,
InitializedNotificationSchema,
]);
export const ClientResultSchema = z.union([
EmptyResultSchema,
CreateMessageResultSchema,
]);
/* Server messages */
export const ServerRequestSchema = z.union([
PingRequestSchema,
CreateMessageRequestSchema,
]);
export const ServerNotificationSchema = z.union([
ProgressNotificationSchema,
LoggingMessageNotificationSchema,
ResourceUpdatedNotificationSchema,
ResourceListChangedNotificationSchema,
ToolListChangedNotificationSchema,
PromptListChangedNotificationSchema,
]);
export const ServerResultSchema = z.union([
EmptyResultSchema,
InitializeResultSchema,
CompleteResultSchema,
GetPromptResultSchema,
ListPromptsResultSchema,
ListResourcesResultSchema,
ReadResourceResultSchema,
CallToolResultSchema,
ListToolsResultSchema,
]);
export class McpError extends Error {
constructor(code, message, data) {
super(`MCP error ${code}: ${message}`);
this.code = code;
this.data = data;
}
}
//# sourceMappingURL=types.js.map

1
packages/mcp-typescript/dist/types.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,19 @@
// @ts-check
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommended,
{
linterOptions: {
reportUnusedDisableDirectives: false,
},
rules: {
"@typescript-eslint/no-unused-vars": ["error",
{ "argsIgnorePattern": "^_" }
]
}
}
);

12
packages/mcp-typescript/jest.config.js generated Normal file
View File

@@ -0,0 +1,12 @@
import { createDefaultEsmPreset } from "ts-jest";
const defaultEsmPreset = createDefaultEsmPreset();
/** @type {import('ts-jest').JestConfigWithTsJest} **/
export default {
...defaultEsmPreset,
moduleNameMapper: {
"^(\\.{1,2}/.*)\\.js$": "$1",
},
testPathIgnorePatterns: ["/node_modules/", "/dist/"],
};

57
packages/mcp-typescript/package.json generated Normal file
View File

@@ -0,0 +1,57 @@
{
"name": "mcp-typescript",
"version": "0.1.0",
"description": "Model Context Protocol implementation for TypeScript",
"private": true,
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
"./*": "./dist/*"
},
"typesVersions": {
"*": {
"*": [
"./dist/*"
]
}
},
"files": [
"dist"
],
"scripts": {
"build": "tsc",
"lint": "eslint src/",
"test": "jest",
"start": "yarn server",
"server": "tsx watch --clear-screen=false src/cli.ts server",
"client": "tsx src/cli.ts client"
},
"dependencies": {
"content-type": "^1.0.5",
"raw-body": "^3.0.0",
"zod": "^3.23.8"
},
"devDependencies": {
"@eslint/js": "^9.8.0",
"@types/content-type": "^1.1.8",
"@types/eslint__js": "^8.42.3",
"@types/eventsource": "^1.1.15",
"@types/express": "^4.17.21",
"@types/jest": "^29.5.12",
"@types/node": "^22.0.2",
"@types/ws": "^8.5.12",
"eslint": "^9.8.0",
"eventsource": "^2.0.2",
"express": "^4.19.2",
"jest": "^29.7.0",
"ts-jest": "^29.2.4",
"tsx": "^4.16.5",
"typescript": "^5.5.4",
"typescript-eslint": "^8.0.0",
"ws": "^8.18.0"
},
"resolutions": {
"strip-ansi": "6.0.1"
}
}

142
packages/mcp-typescript/src/cli.ts generated Normal file
View File

@@ -0,0 +1,142 @@
import EventSource from "eventsource";
import WebSocket from "ws";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(global as any).EventSource = EventSource;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(global as any).WebSocket = WebSocket;
import express from "express";
import { Client } from "./client/index.js";
import { SSEClientTransport } from "./client/sse.js";
import { Server } from "./server/index.js";
import { SSEServerTransport } from "./server/sse.js";
import { WebSocketClientTransport } from "./client/websocket.js";
import { StdioClientTransport } from "./client/stdio.js";
import { StdioServerTransport } from "./server/stdio.js";
async function runClient(url_or_command: string, args: string[]) {
const client = new Client({
name: "mcp-typescript test client",
version: "0.1.0",
});
let clientTransport;
let url: URL | undefined = undefined;
try {
url = new URL(url_or_command);
} catch {
// Ignore
}
if (url?.protocol === "http:" || url?.protocol === "https:") {
clientTransport = new SSEClientTransport();
await clientTransport.connect(new URL(url_or_command));
} else if (url?.protocol === "ws:" || url?.protocol === "wss:") {
clientTransport = new WebSocketClientTransport();
await clientTransport.connect(new URL(url_or_command));
} else {
clientTransport = new StdioClientTransport();
await clientTransport.spawn({
command: url_or_command,
args,
});
}
console.log("Connected to server.");
await client.connect(clientTransport);
console.log("Initialized.");
await client.close();
console.log("Closed.");
}
async function runServer(port: number | null) {
if (port !== null) {
const app = express();
let servers: Server[] = [];
app.get("/sse", async (req, res) => {
console.log("Got new SSE connection");
const transport = new SSEServerTransport("/message");
const server = new Server({
name: "mcp-typescript test server",
version: "0.1.0",
});
servers.push(server);
server.onclose = () => {
console.log("SSE connection closed");
servers = servers.filter((s) => s !== server);
};
await transport.connectSSE(req, res);
await server.connect(transport);
});
app.post("/message", async (req, res) => {
console.log("Received message");
const sessionId = req.query.sessionId as string;
const transport = servers
.map((s) => s.transport as SSEServerTransport)
.find((t) => t.sessionId === sessionId);
if (!transport) {
res.status(404).send("Session not found");
return;
}
await transport.handlePostMessage(req, res);
});
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}/sse`);
});
} else {
const server = new Server({
name: "mcp-typescript test server",
version: "0.1.0",
});
const transport = new StdioServerTransport();
await transport.start();
await server.connect(transport);
console.log("Server running on stdio");
}
}
const args = process.argv.slice(2);
const command = args[0];
switch (command) {
case "client":
if (args.length < 2) {
console.error("Usage: client <server_url_or_command> [args...]");
process.exit(1);
}
runClient(args[1], args.slice(2)).catch((error) => {
console.error(error);
process.exit(1);
});
break;
case "server": {
const port = args[1] ? parseInt(args[1]) : null;
runServer(port).catch((error) => {
console.error(error);
process.exit(1);
});
break;
}
default:
console.error("Unrecognized command:", command);
}

View File

@@ -0,0 +1,79 @@
import { Protocol } from "../shared/protocol.js";
import { Transport } from "../shared/transport.js";
import {
ClientNotification,
ClientRequest,
ClientResult,
Implementation,
InitializeResultSchema,
PROTOCOL_VERSION,
ServerCapabilities,
} from "../types.js";
/**
* An MCP client on top of a pluggable transport.
*
* The client will automatically begin the initialization flow with the server when connect() is called.
*/
export class Client extends Protocol<
ClientRequest,
ClientNotification,
ClientResult
> {
private _serverCapabilities?: ServerCapabilities;
private _serverVersion?: Implementation;
/**
* Initializes this client with the given name and version information.
*/
constructor(private _clientInfo: Implementation) {
super();
}
override async connect(transport: Transport): Promise<void> {
await super.connect(transport);
const result = await this.request(
{
method: "initialize",
params: {
protocolVersion: PROTOCOL_VERSION,
capabilities: {},
clientInfo: this._clientInfo,
},
},
InitializeResultSchema,
);
if (result === undefined) {
throw new Error(`Server sent invalid initialize result: ${result}`);
}
if (result.protocolVersion !== PROTOCOL_VERSION) {
throw new Error(
`Server's protocol version is not supported: ${result.protocolVersion}`,
);
}
this._serverCapabilities = result.capabilities;
this._serverVersion = result.serverInfo;
await this.notification({
method: "notifications/initialized",
});
}
/**
* After initialization has completed, this will be populated with the server's reported capabilities.
*/
getServerCapabilities(): ServerCapabilities | undefined {
return this._serverCapabilities;
}
/**
* After initialization has completed, this will be populated with information about the server's name and version.
*/
getServerVersion(): Implementation | undefined {
return this._serverVersion;
}
}

102
packages/mcp-typescript/src/client/sse.ts generated Normal file
View File

@@ -0,0 +1,102 @@
import { Transport } from "../shared/transport.js";
import { JSONRPCMessage, JSONRPCMessageSchema } from "../types.js";
/**
* Client transport for SSE: this will connect to a server using Server-Sent Events for receiving
* messages and make separate POST requests for sending messages.
*
* This uses the EventSource API in browsers. You can install the `eventsource` package for Node.js.
*/
export class SSEClientTransport implements Transport {
private _eventSource?: EventSource;
private _endpoint?: URL;
private _abortController?: AbortController;
onclose?: () => void;
onerror?: (error: Error) => void;
onmessage?: (message: JSONRPCMessage) => void;
connect(url: URL): Promise<void> {
return new Promise((resolve, reject) => {
this._eventSource = new EventSource(url.href);
this._abortController = new AbortController();
this._eventSource.onerror = (event) => {
const error = new Error(`SSE error: ${JSON.stringify(event)}`);
reject(error);
this.onerror?.(error);
};
this._eventSource.onopen = () => {
// The connection is open, but we need to wait for the endpoint to be received.
};
this._eventSource.addEventListener("endpoint", (event: Event) => {
const messageEvent = event as MessageEvent;
try {
this._endpoint = new URL(messageEvent.data, url);
if (this._endpoint.origin !== url.origin) {
throw new Error(
`Endpoint origin does not match connection origin: ${this._endpoint.origin}`,
);
}
} catch (error) {
reject(error);
this.onerror?.(error as Error);
void this.close();
return;
}
resolve();
});
this._eventSource.onmessage = (event: Event) => {
const messageEvent = event as MessageEvent;
let message: JSONRPCMessage;
try {
message = JSONRPCMessageSchema.parse(JSON.parse(messageEvent.data));
} catch (error) {
this.onerror?.(error as Error);
return;
}
this.onmessage?.(message);
};
});
}
async close(): Promise<void> {
this._abortController?.abort();
this._eventSource?.close();
this.onclose?.();
}
async send(message: JSONRPCMessage): Promise<void> {
if (!this._endpoint) {
throw new Error("Not connected");
}
try {
const response = await fetch(this._endpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(message),
signal: this._abortController?.signal,
});
if (!response.ok) {
const text = await response.text().catch(() => null);
throw new Error(
`Error POSTing to endpoint (HTTP ${response.status}): ${text}`,
);
}
} catch (error) {
this.onerror?.(error as Error);
throw error;
}
}
}

View File

@@ -0,0 +1,61 @@
import { JSONRPCMessage } from "../types.js";
import { StdioClientTransport, StdioServerParameters } from "./stdio.js";
const serverParameters: StdioServerParameters = {
command: "/usr/bin/tee",
};
test("should start then close cleanly", async () => {
const client = new StdioClientTransport();
client.onerror = (error) => {
throw error;
};
let didClose = false;
client.onclose = () => {
didClose = true;
};
await client.spawn(serverParameters);
expect(didClose).toBeFalsy();
await client.close();
expect(didClose).toBeTruthy();
});
test("should read messages", async () => {
const client = new StdioClientTransport();
client.onerror = (error) => {
throw error;
};
const messages: JSONRPCMessage[] = [
{
jsonrpc: "2.0",
id: 1,
method: "ping",
},
{
jsonrpc: "2.0",
method: "notifications/initialized",
},
];
const readMessages: JSONRPCMessage[] = [];
const finished = new Promise<void>((resolve) => {
client.onmessage = (message) => {
readMessages.push(message);
if (JSON.stringify(message) === JSON.stringify(messages[1])) {
resolve();
}
};
});
await client.spawn(serverParameters);
await client.send(messages[0]);
await client.send(messages[1]);
await finished;
expect(readMessages).toEqual(messages);
await client.close();
});

View File

@@ -0,0 +1,121 @@
import { ChildProcess, spawn } from "node:child_process";
import { ReadBuffer, serializeMessage } from "../shared/stdio.js";
import { JSONRPCMessage } from "../types.js";
import { Transport } from "../shared/transport.js";
export type StdioServerParameters = {
/**
* The executable to run to start the server.
*/
command: string;
/**
* Command line arguments to pass to the executable.
*/
args?: string[];
/**
* The environment to use when spawning the process.
*
* The environment is NOT inherited from the parent process by default.
*/
env?: object;
};
/**
* Client transport for stdio: this will connect to a server by spawning a process and communicating with it over stdin/stdout.
*
* This transport is only available in Node.js environments.
*/
export class StdioClientTransport implements Transport {
private _process?: ChildProcess;
private _abortController: AbortController = new AbortController();
private _readBuffer: ReadBuffer = new ReadBuffer();
onclose?: () => void;
onerror?: (error: Error) => void;
onmessage?: (message: JSONRPCMessage) => void;
/**
* Spawns the server process and prepare to communicate with it.
*/
spawn(server: StdioServerParameters): Promise<void> {
return new Promise((resolve, reject) => {
this._process = spawn(server.command, server.args ?? [], {
// The parent process may have sensitive secrets in its env, so don't inherit it automatically.
env: server.env === undefined ? {} : { ...server.env },
stdio: ["pipe", "pipe", "inherit"],
signal: this._abortController.signal,
});
this._process.on("error", (error) => {
if (error.name === "AbortError") {
// Expected when close() is called.
this.onclose?.();
return;
}
reject(error);
this.onerror?.(error);
});
this._process.on("spawn", () => {
resolve();
});
this._process.on("close", (_code) => {
this._process = undefined;
this.onclose?.();
});
this._process.stdin?.on("error", (error) => {
this.onerror?.(error);
});
this._process.stdout?.on("data", (chunk) => {
this._readBuffer.append(chunk);
this.processReadBuffer();
});
this._process.stdout?.on("error", (error) => {
this.onerror?.(error);
});
});
}
private processReadBuffer() {
while (true) {
try {
const message = this._readBuffer.readMessage();
if (message === null) {
break;
}
this.onmessage?.(message);
} catch (error) {
this.onerror?.(error as Error);
}
}
}
async close(): Promise<void> {
this._abortController.abort();
this._process = undefined;
this._readBuffer.clear();
}
send(message: JSONRPCMessage): Promise<void> {
return new Promise((resolve) => {
if (!this._process?.stdin) {
throw new Error("Not connected");
}
const json = serializeMessage(message);
if (this._process.stdin.write(json)) {
resolve();
} else {
this._process.stdin.once("drain", resolve);
}
});
}
}

View File

@@ -0,0 +1,66 @@
import { Transport } from "../shared/transport.js";
import { JSONRPCMessage, JSONRPCMessageSchema } from "../types.js";
const SUBPROTOCOL = "mcp";
/**
* Client transport for WebSocket: this will connect to a server over the WebSocket protocol.
*/
export class WebSocketClientTransport implements Transport {
private _socket?: WebSocket;
onclose?: () => void;
onerror?: (error: Error) => void;
onmessage?: (message: JSONRPCMessage) => void;
connect(url: URL): Promise<void> {
return new Promise((resolve, reject) => {
this._socket = new WebSocket(url, SUBPROTOCOL);
this._socket.onerror = (event) => {
const error =
"error" in event
? (event.error as Error)
: new Error(`WebSocket error: ${JSON.stringify(event)}`);
reject(error);
this.onerror?.(error);
};
this._socket.onopen = () => {
resolve();
};
this._socket.onclose = () => {
this.onclose?.();
};
this._socket.onmessage = (event: MessageEvent) => {
let message: JSONRPCMessage;
try {
message = JSONRPCMessageSchema.parse(JSON.parse(event.data));
} catch (error) {
this.onerror?.(error as Error);
return;
}
this.onmessage?.(message);
};
});
}
async close(): Promise<void> {
this._socket?.close();
}
send(message: JSONRPCMessage): Promise<void> {
return new Promise((resolve, reject) => {
if (!this._socket) {
reject(new Error("Not connected"));
return;
}
this._socket?.send(JSON.stringify(message));
resolve();
});
}
}

View File

@@ -0,0 +1,79 @@
import { Protocol } from "../shared/protocol.js";
import {
ClientCapabilities,
Implementation,
InitializedNotificationSchema,
InitializeRequest,
InitializeRequestSchema,
InitializeResult,
PROTOCOL_VERSION,
ServerNotification,
ServerRequest,
ServerResult,
} from "../types.js";
/**
* An MCP server on top of a pluggable transport.
*
* This server will automatically respond to the initialization flow as initiated from the client.
*/
export class Server extends Protocol<
ServerRequest,
ServerNotification,
ServerResult
> {
private _clientCapabilities?: ClientCapabilities;
private _clientVersion?: Implementation;
/**
* Callback for when initialization has fully completed (i.e., the client has sent an `initialized` notification).
*/
oninitialized?: () => void;
/**
* Initializes this server with the given name and version information.
*/
constructor(private _serverInfo: Implementation) {
super();
this.setRequestHandler(InitializeRequestSchema, (request) =>
this._oninitialize(request),
);
this.setNotificationHandler(InitializedNotificationSchema, () =>
this.oninitialized?.(),
);
}
private async _oninitialize(
request: InitializeRequest,
): Promise<InitializeResult> {
if (request.params.protocolVersion !== PROTOCOL_VERSION) {
throw new Error(
`Client's protocol version is not supported: ${request.params.protocolVersion}`,
);
}
this._clientCapabilities = request.params.capabilities;
this._clientVersion = request.params.clientInfo;
return {
protocolVersion: PROTOCOL_VERSION,
capabilities: {},
serverInfo: this._serverInfo,
};
}
/**
* After initialization has completed, this will be populated with the client's reported capabilities.
*/
getClientCapabilities(): ClientCapabilities | undefined {
return this._clientCapabilities;
}
/**
* After initialization has completed, this will be populated with information about the client's name and version.
*/
getClientVersion(): Implementation | undefined {
return this._clientVersion;
}
}

139
packages/mcp-typescript/src/server/sse.ts generated Normal file
View File

@@ -0,0 +1,139 @@
import { randomUUID } from "node:crypto";
import { IncomingMessage, ServerResponse } from "node:http";
import { Transport } from "../shared/transport.js";
import { JSONRPCMessage, JSONRPCMessageSchema } from "../types.js";
import getRawBody from "raw-body";
import contentType from "content-type";
const MAXIMUM_MESSAGE_SIZE = "4mb";
/**
* Server transport for SSE: this will send messages over an SSE connection and receive messages from HTTP POST requests.
*
* This transport is only available in Node.js environments.
*/
export class SSEServerTransport implements Transport {
private _sseResponse?: ServerResponse;
private _sessionId: string;
onclose?: () => void;
onerror?: (error: Error) => void;
onmessage?: (message: JSONRPCMessage) => void;
/**
* Creates a new SSE server transport, which will direct the client to POST messages to the relative or absolute URL identified by `_endpoint`.
*/
constructor(private _endpoint: string) {
this._sessionId = randomUUID();
}
/**
* Handles the initial SSE connection request.
*
* This should be called when a GET request is made to establish the SSE stream.
*/
async connectSSE(req: IncomingMessage, res: ServerResponse): Promise<void> {
if (this._sseResponse) {
throw new Error("Already connected!");
}
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
});
// Send the endpoint event
res.write(
`event: endpoint\ndata: ${encodeURI(this._endpoint)}?sessionId=${this._sessionId}\n\n`,
);
this._sseResponse = res;
res.on("close", () => {
this._sseResponse = undefined;
this.onclose?.();
});
}
/**
* Handles incoming POST messages.
*
* This should be called when a POST request is made to send a message to the server.
*/
async handlePostMessage(
req: IncomingMessage,
res: ServerResponse,
): Promise<void> {
if (!this._sseResponse) {
const message = "SSE connection not established";
res.writeHead(500).end(message);
throw new Error(message);
}
let body: string;
try {
const ct = contentType.parse(req.headers["content-type"] ?? "");
if (ct.type !== "application/json") {
throw new Error(`Unsupported content-type: ${ct}`);
}
body = await getRawBody(req, {
limit: MAXIMUM_MESSAGE_SIZE,
encoding: ct.parameters.charset ?? "utf-8",
});
} catch (error) {
res.writeHead(400).end(String(error));
this.onerror?.(error as Error);
return;
}
try {
await this.handleMessage(JSON.parse(body));
} catch {
res.writeHead(400).end(`Invalid message: ${body}`);
return;
}
res.writeHead(202).end("Accepted");
}
/**
* Handle a client message, regardless of how it arrived. This can be used to inform the server of messages that arrive via a means different than HTTP POST.
*/
async handleMessage(message: unknown): Promise<void> {
let parsedMessage: JSONRPCMessage;
try {
parsedMessage = JSONRPCMessageSchema.parse(message);
} catch (error) {
this.onerror?.(error as Error);
throw error;
}
this.onmessage?.(parsedMessage);
}
async close(): Promise<void> {
this._sseResponse?.end();
this._sseResponse = undefined;
this.onclose?.();
}
async send(message: JSONRPCMessage): Promise<void> {
if (!this._sseResponse) {
throw new Error("Not connected");
}
this._sseResponse.write(
`event: message\ndata: ${JSON.stringify(message)}\n\n`,
);
}
/**
* Returns the session ID for this transport.
*
* This can be used to route incoming POST requests.
*/
get sessionId(): string {
return this._sessionId;
}
}

View File

@@ -0,0 +1,102 @@
import { Readable, Writable } from "node:stream";
import { ReadBuffer, serializeMessage } from "../shared/stdio.js";
import { JSONRPCMessage } from "../types.js";
import { StdioServerTransport } from "./stdio.js";
let input: Readable;
let outputBuffer: ReadBuffer;
let output: Writable;
beforeEach(() => {
input = new Readable({
// We'll use input.push() instead.
read: () => {},
});
outputBuffer = new ReadBuffer();
output = new Writable({
write(chunk, encoding, callback) {
outputBuffer.append(chunk);
callback();
},
});
});
test("should start then close cleanly", async () => {
const server = new StdioServerTransport(input, output);
server.onerror = (error) => {
throw error;
};
let didClose = false;
server.onclose = () => {
didClose = true;
};
await server.start();
expect(didClose).toBeFalsy();
await server.close();
expect(didClose).toBeTruthy();
});
test("should not read until started", async () => {
const server = new StdioServerTransport(input, output);
server.onerror = (error) => {
throw error;
};
let didRead = false;
const readMessage = new Promise((resolve) => {
server.onmessage = (message) => {
didRead = true;
resolve(message);
};
});
const message: JSONRPCMessage = {
jsonrpc: "2.0",
id: 1,
method: "ping",
};
input.push(serializeMessage(message));
expect(didRead).toBeFalsy();
await server.start();
expect(await readMessage).toEqual(message);
});
test("should read multiple messages", async () => {
const server = new StdioServerTransport(input, output);
server.onerror = (error) => {
throw error;
};
const messages: JSONRPCMessage[] = [
{
jsonrpc: "2.0",
id: 1,
method: "ping",
},
{
jsonrpc: "2.0",
method: "notifications/initialized",
},
];
const readMessages: JSONRPCMessage[] = [];
const finished = new Promise<void>((resolve) => {
server.onmessage = (message) => {
readMessages.push(message);
if (JSON.stringify(message) === JSON.stringify(messages[1])) {
resolve();
}
};
});
input.push(serializeMessage(messages[0]));
input.push(serializeMessage(messages[1]));
await server.start();
await finished;
expect(readMessages).toEqual(messages);
});

View File

@@ -0,0 +1,73 @@
import process from "node:process";
import { Readable, Writable } from "node:stream";
import { ReadBuffer, serializeMessage } from "../shared/stdio.js";
import { JSONRPCMessage } from "../types.js";
import { Transport } from "../shared/transport.js";
/**
* Server transport for stdio: this communicates with a MCP client by reading from the current process' stdin and writing to stdout.
*
* This transport is only available in Node.js environments.
*/
export class StdioServerTransport implements Transport {
private _readBuffer: ReadBuffer = new ReadBuffer();
constructor(
private _stdin: Readable = process.stdin,
private _stdout: Writable = process.stdout,
) {}
onclose?: () => void;
onerror?: (error: Error) => void;
onmessage?: (message: JSONRPCMessage) => void;
// Arrow functions to bind `this` properly, while maintaining function identity.
_ondata = (chunk: Buffer) => {
this._readBuffer.append(chunk);
this.processReadBuffer();
};
_onerror = (error: Error) => {
this.onerror?.(error);
};
/**
* Starts listening for messages on stdin.
*/
async start(): Promise<void> {
this._stdin.on("data", this._ondata);
this._stdin.on("error", this._onerror);
}
private processReadBuffer() {
while (true) {
try {
const message = this._readBuffer.readMessage();
if (message === null) {
break;
}
this.onmessage?.(message);
} catch (error) {
this.onerror?.(error as Error);
}
}
}
async close(): Promise<void> {
this._stdin.off("data", this._ondata);
this._stdin.off("error", this._onerror);
this._readBuffer.clear();
this.onclose?.();
}
send(message: JSONRPCMessage): Promise<void> {
return new Promise((resolve) => {
const json = serializeMessage(message);
if (this._stdout.write(json)) {
resolve();
} else {
this._stdout.once("drain", resolve);
}
});
}
}

View File

@@ -0,0 +1,361 @@
import { AnyZodObject, ZodLiteral, ZodObject, z } from "zod";
import {
ErrorCode,
JSONRPCError,
JSONRPCNotification,
JSONRPCRequest,
JSONRPCResponse,
McpError,
Notification,
PingRequestSchema,
Progress,
ProgressNotification,
ProgressNotificationSchema,
Request,
Result,
} from "../types.js";
import { Transport } from "./transport.js";
/**
* Callback for progress notifications.
*/
export type ProgressCallback = (progress: Progress) => void;
/**
* Implements MCP protocol framing on top of a pluggable transport, including
* features like request/response linking, notifications, and progress.
*/
export class Protocol<
SendRequestT extends Request,
SendNotificationT extends Notification,
SendResultT extends Result,
> {
private _transport?: Transport;
private _requestMessageId = 0;
private _requestHandlers: Map<
string,
(request: JSONRPCRequest) => Promise<SendResultT>
> = new Map();
private _notificationHandlers: Map<
string,
(notification: JSONRPCNotification) => Promise<void>
> = new Map();
private _responseHandlers: Map<
number,
(response: JSONRPCResponse | Error) => void
> = new Map();
private _progressHandlers: Map<number, ProgressCallback> = new Map();
/**
* Callback for when the connection is closed for any reason.
*
* This is invoked when close() is called as well.
*/
onclose?: () => void;
/**
* Callback for when an error occurs.
*
* Note that errors are not necessarily fatal; they are used for reporting any kind of exceptional condition out of band.
*/
onerror?: (error: Error) => void;
/**
* A handler to invoke for any request types that do not have their own handler installed.
*/
fallbackRequestHandler?: (request: Request) => Promise<SendResultT>;
/**
* A handler to invoke for any notification types that do not have their own handler installed.
*/
fallbackNotificationHandler?: (notification: Notification) => Promise<void>;
constructor() {
this.setNotificationHandler(ProgressNotificationSchema, (notification) => {
this._onprogress(notification as unknown as ProgressNotification);
});
this.setRequestHandler(
PingRequestSchema,
// Automatic pong by default.
(_request) => ({}) as SendResultT,
);
}
/**
* Attaches to the given transport and starts listening for messages.
*
* The Protocol object assumes ownership of the Transport, replacing any callbacks that have already been set, and expects that it is the only user of the Transport instance going forward.
*/
async connect(transport: Transport): Promise<void> {
this._transport = transport;
this._transport.onclose = () => {
this._onclose();
};
this._transport.onerror = (error: Error) => {
this._onerror(error);
};
this._transport.onmessage = (message) => {
if (!("method" in message)) {
this._onresponse(message);
} else if ("id" in message) {
this._onrequest(message);
} else {
this._onnotification(message);
}
};
}
private _onclose(): void {
const responseHandlers = this._responseHandlers;
this._responseHandlers = new Map();
this._progressHandlers.clear();
this._transport = undefined;
this.onclose?.();
const error = new McpError(ErrorCode.ConnectionClosed, "Connection closed");
for (const handler of responseHandlers.values()) {
handler(error);
}
}
private _onerror(error: Error): void {
this.onerror?.(error);
}
private _onnotification(notification: JSONRPCNotification): void {
const handler =
this._notificationHandlers.get(notification.method) ??
this.fallbackNotificationHandler;
// Ignore notifications not being subscribed to.
if (handler === undefined) {
return;
}
handler(notification).catch((error) =>
this._onerror(
new Error(`Uncaught error in notification handler: ${error}`),
),
);
}
private _onrequest(request: JSONRPCRequest): void {
const handler =
this._requestHandlers.get(request.method) ?? this.fallbackRequestHandler;
if (handler === undefined) {
this._transport
?.send({
jsonrpc: "2.0",
id: request.id,
error: {
code: ErrorCode.MethodNotFound,
message: "Method not found",
},
})
.catch((error) =>
this._onerror(
new Error(`Failed to send an error response: ${error}`),
),
);
return;
}
handler(request)
.then(
(result) => {
this._transport?.send({
result,
jsonrpc: "2.0",
id: request.id,
});
},
(error) => {
return this._transport?.send({
jsonrpc: "2.0",
id: request.id,
error: {
code: error["code"]
? Math.floor(Number(error["code"]))
: ErrorCode.InternalError,
message: error.message ?? "Internal error",
},
});
},
)
.catch((error) =>
this._onerror(new Error(`Failed to send response: ${error}`)),
);
}
private _onprogress(notification: ProgressNotification): void {
const { progress, total, progressToken } = notification.params;
const handler = this._progressHandlers.get(Number(progressToken));
if (handler === undefined) {
this._onerror(
new Error(
`Received a progress notification for an unknown token: ${JSON.stringify(notification)}`,
),
);
return;
}
handler({ progress, total });
}
private _onresponse(response: JSONRPCResponse | JSONRPCError): void {
const messageId = response.id;
const handler = this._responseHandlers.get(Number(messageId));
if (handler === undefined) {
this._onerror(
new Error(
`Received a response for an unknown message ID: ${JSON.stringify(response)}`,
),
);
return;
}
this._responseHandlers.delete(Number(messageId));
this._progressHandlers.delete(Number(messageId));
if ("result" in response) {
handler(response);
} else {
const error = new McpError(
response.error.code,
response.error.message,
response.error.data,
);
handler(error);
}
}
get transport(): Transport | undefined {
return this._transport;
}
/**
* Closes the connection.
*/
async close(): Promise<void> {
await this._transport?.close();
}
/**
* Sends a request and wait for a response, with optional progress notifications in the meantime (if supported by the server).
*
* Do not use this method to emit notifications! Use notification() instead.
*/
request<T extends AnyZodObject>(
request: SendRequestT,
resultSchema: T,
onprogress?: ProgressCallback,
): Promise<z.infer<T>> {
return new Promise((resolve, reject) => {
if (!this._transport) {
reject(new Error("Not connected"));
return;
}
const messageId = this._requestMessageId++;
const jsonrpcRequest: JSONRPCRequest = {
...request,
jsonrpc: "2.0",
id: messageId,
};
if (onprogress) {
this._progressHandlers.set(messageId, onprogress);
jsonrpcRequest.params = {
...request.params,
_meta: { progressToken: messageId },
};
}
this._responseHandlers.set(messageId, (response) => {
if (response instanceof Error) {
return reject(response);
}
try {
const result = resultSchema.parse(response.result);
resolve(result);
} catch (error) {
reject(error);
}
});
this._transport.send(jsonrpcRequest).catch(reject);
});
}
/**
* Emits a notification, which is a one-way message that does not expect a response.
*/
async notification(notification: SendNotificationT): Promise<void> {
if (!this._transport) {
throw new Error("Not connected");
}
const jsonrpcNotification: JSONRPCNotification = {
...notification,
jsonrpc: "2.0",
};
await this._transport.send(jsonrpcNotification);
}
/**
* Registers a handler to invoke when this protocol object receives a request with the given method.
*
* Note that this will replace any previous request handler for the same method.
*/
setRequestHandler<
T extends ZodObject<{
method: ZodLiteral<string>;
}>,
>(
requestSchema: T,
handler: (request: z.infer<T>) => SendResultT | Promise<SendResultT>,
): void {
this._requestHandlers.set(requestSchema.shape.method.value, (request) =>
Promise.resolve(handler(requestSchema.parse(request))),
);
}
/**
* Removes the request handler for the given method.
*/
removeRequestHandler(method: string): void {
this._requestHandlers.delete(method);
}
/**
* Registers a handler to invoke when this protocol object receives a notification with the given method.
*
* Note that this will replace any previous notification handler for the same method.
*/
setNotificationHandler<
T extends ZodObject<{
method: ZodLiteral<string>;
}>,
>(
notificationSchema: T,
handler: (notification: z.infer<T>) => void | Promise<void>,
): void {
this._notificationHandlers.set(
notificationSchema.shape.method.value,
(notification) =>
Promise.resolve(handler(notificationSchema.parse(notification))),
);
}
/**
* Removes the notification handler for the given method.
*/
removeNotificationHandler(method: string): void {
this._notificationHandlers.delete(method);
}
}

View File

@@ -0,0 +1,35 @@
import { JSONRPCMessage } from "../types.js";
import { ReadBuffer } from "./stdio.js";
const testMessage: JSONRPCMessage = {
jsonrpc: "2.0",
method: "foobar",
};
test("should have no messages after initialization", () => {
const readBuffer = new ReadBuffer();
expect(readBuffer.readMessage()).toBeNull();
});
test("should only yield a message after a newline", () => {
const readBuffer = new ReadBuffer();
readBuffer.append(Buffer.from(JSON.stringify(testMessage)));
expect(readBuffer.readMessage()).toBeNull();
readBuffer.append(Buffer.from("\n"));
expect(readBuffer.readMessage()).toEqual(testMessage);
expect(readBuffer.readMessage()).toBeNull();
});
test("should be reusable after clearing", () => {
const readBuffer = new ReadBuffer();
readBuffer.append(Buffer.from("foobar"));
readBuffer.clear();
expect(readBuffer.readMessage()).toBeNull();
readBuffer.append(Buffer.from(JSON.stringify(testMessage)));
readBuffer.append(Buffer.from("\n"));
expect(readBuffer.readMessage()).toEqual(testMessage);
});

View File

@@ -0,0 +1,39 @@
import { JSONRPCMessage, JSONRPCMessageSchema } from "../types.js";
/**
* Buffers a continuous stdio stream into discrete JSON-RPC messages.
*/
export class ReadBuffer {
private _buffer?: Buffer;
append(chunk: Buffer): void {
this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
}
readMessage(): JSONRPCMessage | null {
if (!this._buffer) {
return null;
}
const index = this._buffer.indexOf("\n");
if (index === -1) {
return null;
}
const line = this._buffer.toString("utf8", 0, index);
this._buffer = this._buffer.subarray(index + 1);
return deserializeMessage(line);
}
clear(): void {
this._buffer = undefined;
}
}
export function deserializeMessage(line: string): JSONRPCMessage {
return JSONRPCMessageSchema.parse(JSON.parse(line));
}
export function serializeMessage(message: JSONRPCMessage): string {
return JSON.stringify(message) + "\n";
}

Some files were not shown because too many files have changed in this diff Show More