Compare commits
40 Commits
ashwin/res
...
davidsp/em
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b3f967867 | ||
|
|
752631f5a1 | ||
|
|
2acc6290c8 | ||
|
|
5427a6ec7c | ||
|
|
daa329b3bd | ||
|
|
a5606f7ac7 | ||
|
|
7926eea39c | ||
|
|
a798c5528e | ||
|
|
8ff82d7f78 | ||
|
|
69a4a7461d | ||
|
|
c4628d27cd | ||
|
|
702f827f74 | ||
|
|
82f3d99639 | ||
|
|
0d66867701 | ||
|
|
5da417470f | ||
|
|
b4c70edb78 | ||
|
|
bfa23c9de8 | ||
|
|
f40854fa74 | ||
|
|
956e2b4b42 | ||
|
|
6239453604 | ||
|
|
6acd606a7e | ||
|
|
a16649be87 | ||
|
|
dcdcfd284f | ||
|
|
567c15a1cf | ||
|
|
dca7c7766b | ||
|
|
7680d5272a | ||
|
|
827ee4a283 | ||
|
|
0e60829bcc | ||
|
|
9f5366c49c | ||
|
|
7bb40c188f | ||
|
|
9ff362dd85 | ||
|
|
24d005ac84 | ||
|
|
5f9d11b3a9 | ||
|
|
cc1bf029fa | ||
|
|
247dae54b3 | ||
|
|
48355e9cb2 | ||
|
|
3b4d8136b2 | ||
|
|
23ca80ed66 | ||
|
|
0ffedbf8fa | ||
|
|
c6a11422f4 |
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -1,2 +1 @@
|
||||
yarn.lock linguist-generated=true
|
||||
packages/**/* linguist-generated=true
|
||||
package-lock.json linguist-generated=true
|
||||
|
||||
37
.github/workflows/main.yml
vendored
Normal file
37
.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
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
1
.gitignore
vendored
@@ -3,3 +3,4 @@ node_modules
|
||||
server/build
|
||||
client/dist
|
||||
client/tsconfig.app.tsbuildinfo
|
||||
client/tsconfig.node.tsbuildinfo
|
||||
|
||||
19
README.md
19
README.md
@@ -2,16 +2,23 @@
|
||||
|
||||
The MCP inspector is a developer tool for testing and debugging MCP servers.
|
||||
|
||||
Setup:
|
||||
## Getting started
|
||||
|
||||
```bash
|
||||
yarn
|
||||
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:
|
||||
|
||||
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:
|
||||
|
||||
```bash
|
||||
yarn dev
|
||||
npm run dev
|
||||
```
|
||||
|
||||
This will start both the client and server.
|
||||
@@ -19,6 +26,6 @@ This will start both the client and server.
|
||||
To run in production mode:
|
||||
|
||||
```bash
|
||||
yarn build
|
||||
yarn start
|
||||
npm run build
|
||||
npm start
|
||||
```
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React + TS</title>
|
||||
<title>MCP Inspector</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "*",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-label": "^2.1.0",
|
||||
"@radix-ui/react-select": "^2.1.2",
|
||||
@@ -18,11 +19,11 @@
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.447.0",
|
||||
"mcp-typescript": "file:../packages/mcp-typescript",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"tailwind-merge": "^2.5.3",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.11.1",
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
#root {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
|
||||
@@ -1,28 +1,25 @@
|
||||
import { Client } from "mcp-typescript/client/index.js";
|
||||
import { SSEClientTransport } from "mcp-typescript/client/sse.js";
|
||||
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
||||
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
||||
import {
|
||||
ListResourcesResultSchema,
|
||||
GetPromptResultSchema,
|
||||
ListToolsResultSchema,
|
||||
ReadResourceResultSchema,
|
||||
CallToolResultSchema,
|
||||
ListPromptsResultSchema,
|
||||
Tool,
|
||||
ClientRequest,
|
||||
} from "mcp-typescript/types.js";
|
||||
import { useState } from "react";
|
||||
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";
|
||||
CreateMessageRequestSchema,
|
||||
CreateMessageResult,
|
||||
EmptyResultSchema,
|
||||
GetPromptResultSchema,
|
||||
ListPromptsResultSchema,
|
||||
ListResourcesResultSchema,
|
||||
ListToolsResultSchema,
|
||||
ProgressNotificationSchema,
|
||||
ReadResourceResultSchema,
|
||||
Resource,
|
||||
ServerNotification,
|
||||
Tool,
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -30,16 +27,29 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} 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 Sidebar from "./components/Sidebar";
|
||||
import RequestsTab from "./components/RequestsTabs";
|
||||
import ResourcesTab, { Resource } from "./components/ResourcesTab";
|
||||
import NotificationsTab from "./components/NotificationsTab";
|
||||
import HistoryAndNotifications from "./components/History";
|
||||
import PingTab from "./components/PingTab";
|
||||
import PromptsTab, { Prompt } from "./components/PromptsTab";
|
||||
import RequestsTab from "./components/RequestsTabs";
|
||||
import ResourcesTab from "./components/ResourcesTab";
|
||||
import SamplingTab, { PendingRequest } from "./components/SamplingTab";
|
||||
import Sidebar from "./components/Sidebar";
|
||||
import ToolsTab from "./components/ToolsTab";
|
||||
import History from "./components/History";
|
||||
import { AnyZodObject } from "node_modules/zod/lib";
|
||||
|
||||
const App = () => {
|
||||
const [connectionStatus, setConnectionStatus] = useState<
|
||||
@@ -52,24 +62,70 @@ const App = () => {
|
||||
const [tools, setTools] = useState<Tool[]>([]);
|
||||
const [toolResult, setToolResult] = useState<string>("");
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [command, setCommand] = useState<string>(
|
||||
"/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 [command, setCommand] = useState<string>(() => {
|
||||
return (
|
||||
localStorage.getItem("lastCommand") ||
|
||||
"/Users/ashwin/.nvm/versions/node/v18.20.4/bin/node"
|
||||
);
|
||||
});
|
||||
const [args, setArgs] = useState<string>(() => {
|
||||
return localStorage.getItem("lastArgs") || "";
|
||||
});
|
||||
const [url, setUrl] = useState<string>("http://localhost:3001/sse");
|
||||
const [transportType, setTransportType] = useState<"stdio" | "sse">("stdio");
|
||||
const [requestHistory, setRequestHistory] = useState<
|
||||
{ request: string; response: string }[]
|
||||
>([]);
|
||||
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>(
|
||||
null,
|
||||
);
|
||||
const [selectedPrompt, setSelectedPrompt] = useState<Prompt | 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) => {
|
||||
setRequestHistory((prev) => [
|
||||
@@ -100,12 +156,12 @@ const App = () => {
|
||||
const response = await makeRequest(
|
||||
{
|
||||
method: "resources/list" as const,
|
||||
params: nextResourceCursor ? { cursor: nextResourceCursor } : {},
|
||||
},
|
||||
ListResourcesResultSchema,
|
||||
);
|
||||
if (response.resources) {
|
||||
setResources(response.resources);
|
||||
}
|
||||
setResources(resources.concat(response.resources ?? []));
|
||||
setNextResourceCursor(response.nextCursor);
|
||||
};
|
||||
|
||||
const readResource = async (uri: string) => {
|
||||
@@ -123,10 +179,12 @@ const App = () => {
|
||||
const response = await makeRequest(
|
||||
{
|
||||
method: "prompts/list" as const,
|
||||
params: nextPromptCursor ? { cursor: nextPromptCursor } : {},
|
||||
},
|
||||
ListPromptsResultSchema,
|
||||
);
|
||||
setPrompts(response.prompts);
|
||||
setNextPromptCursor(response.nextCursor);
|
||||
};
|
||||
|
||||
const getPrompt = async (name: string, args: Record<string, string> = {}) => {
|
||||
@@ -144,17 +202,25 @@ const App = () => {
|
||||
const response = await makeRequest(
|
||||
{
|
||||
method: "tools/list" as const,
|
||||
params: nextToolCursor ? { cursor: nextToolCursor } : {},
|
||||
},
|
||||
ListToolsResultSchema,
|
||||
);
|
||||
setTools(response.tools);
|
||||
setNextToolCursor(response.nextCursor);
|
||||
};
|
||||
|
||||
const callTool = async (name: string, params: Record<string, unknown>) => {
|
||||
const response = await makeRequest(
|
||||
{
|
||||
method: "tools/call" as const,
|
||||
params: { name, arguments: params },
|
||||
params: {
|
||||
name,
|
||||
arguments: params,
|
||||
_meta: {
|
||||
progressToken: progressTokenRef.current++,
|
||||
},
|
||||
},
|
||||
},
|
||||
CallToolResultSchema,
|
||||
);
|
||||
@@ -168,7 +234,6 @@ const App = () => {
|
||||
version: "0.0.1",
|
||||
});
|
||||
|
||||
const clientTransport = new SSEClientTransport();
|
||||
const backendUrl = new URL("http://localhost:3000/sse");
|
||||
|
||||
backendUrl.searchParams.append("transportType", transportType);
|
||||
@@ -179,9 +244,28 @@ const App = () => {
|
||||
backendUrl.searchParams.append("url", url);
|
||||
}
|
||||
|
||||
await clientTransport.connect(backendUrl);
|
||||
const clientTransport = new SSEClientTransport(backendUrl);
|
||||
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);
|
||||
setConnectionStatus("connected");
|
||||
} catch (e) {
|
||||
@@ -255,10 +339,6 @@ const App = () => {
|
||||
<Send className="w-4 h-4 mr-2" />
|
||||
Requests
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="notifications" disabled>
|
||||
<Bell className="w-4 h-4 mr-2" />
|
||||
Notifications
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="tools">
|
||||
<Hammer className="w-4 h-4 mr-2" />
|
||||
Tools
|
||||
@@ -267,6 +347,19 @@ const App = () => {
|
||||
<Terminal className="w-4 h-4 mr-2" />
|
||||
Console
|
||||
</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>
|
||||
|
||||
<div className="w-full">
|
||||
@@ -277,9 +370,9 @@ const App = () => {
|
||||
selectedResource={selectedResource}
|
||||
setSelectedResource={setSelectedResource}
|
||||
resourceContent={resourceContent}
|
||||
nextCursor={nextResourceCursor}
|
||||
error={error}
|
||||
/>
|
||||
<NotificationsTab />
|
||||
<PromptsTab
|
||||
prompts={prompts}
|
||||
listPrompts={listPrompts}
|
||||
@@ -287,6 +380,7 @@ const App = () => {
|
||||
selectedPrompt={selectedPrompt}
|
||||
setSelectedPrompt={setSelectedPrompt}
|
||||
promptContent={promptContent}
|
||||
nextCursor={nextPromptCursor}
|
||||
error={error}
|
||||
/>
|
||||
<RequestsTab />
|
||||
@@ -300,9 +394,25 @@ const App = () => {
|
||||
setToolResult("");
|
||||
}}
|
||||
toolResult={toolResult}
|
||||
nextCursor={nextToolCursor}
|
||||
error={error}
|
||||
/>
|
||||
<ConsoleTab />
|
||||
<PingTab
|
||||
onPingClick={() => {
|
||||
void makeRequest(
|
||||
{
|
||||
method: "ping" as const,
|
||||
},
|
||||
EmptyResultSchema,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<SamplingTab
|
||||
pendingRequests={pendingSampleRequests}
|
||||
onApprove={handleApproveSampling}
|
||||
onReject={handleRejectSampling}
|
||||
/>
|
||||
</div>
|
||||
</Tabs>
|
||||
) : (
|
||||
@@ -315,7 +425,10 @@ const App = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<History requestHistory={requestHistory} />
|
||||
<HistoryAndNotifications
|
||||
requestHistory={requestHistory}
|
||||
serverNotifications={notifications}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,94 +1,163 @@
|
||||
import { useState } from "react";
|
||||
import { ServerNotification } from "@modelcontextprotocol/sdk/types.js";
|
||||
import { Copy } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
const History = ({
|
||||
const HistoryAndNotifications = ({
|
||||
requestHistory,
|
||||
serverNotifications,
|
||||
}: {
|
||||
requestHistory: Array<{ request: string; response: string | null }>;
|
||||
serverNotifications: ServerNotification[];
|
||||
}) => {
|
||||
const [expandedRequests, setExpandedRequests] = useState<{
|
||||
[key: number]: boolean;
|
||||
}>({});
|
||||
const [expandedNotifications, setExpandedNotifications] = useState<{
|
||||
[key: number]: boolean;
|
||||
}>({});
|
||||
|
||||
const toggleRequestExpansion = (index: number) => {
|
||||
setExpandedRequests((prev) => ({ ...prev, [index]: !prev[index] }));
|
||||
};
|
||||
|
||||
const toggleNotificationExpansion = (index: number) => {
|
||||
setExpandedNotifications((prev) => ({ ...prev, [index]: !prev[index] }));
|
||||
};
|
||||
|
||||
const copyToClipboard = (text: string) => {
|
||||
navigator.clipboard.writeText(text);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-64 bg-white shadow-md p-4 overflow-y-auto">
|
||||
<h2 className="text-lg font-semibold mb-4">History</h2>
|
||||
<ul className="space-y-3">
|
||||
{requestHistory
|
||||
.slice()
|
||||
.reverse()
|
||||
.map((request, 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={() =>
|
||||
toggleRequestExpansion(requestHistory.length - 1 - index)
|
||||
}
|
||||
>
|
||||
<span className="font-mono">
|
||||
{requestHistory.length - index}.{" "}
|
||||
{JSON.parse(request.request).method}
|
||||
</span>
|
||||
<span>
|
||||
{expandedRequests[requestHistory.length - 1 - index]
|
||||
? "▼"
|
||||
: "▶"}
|
||||
</span>
|
||||
</div>
|
||||
{expandedRequests[requestHistory.length - 1 - index] && (
|
||||
<>
|
||||
<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 className="w-64 bg-white shadow-md p-4 overflow-hidden flex flex-col h-full">
|
||||
<div className="flex-1 overflow-y-auto mb-4 border-b pb-4">
|
||||
<h2 className="text-lg font-semibold mb-4">History</h2>
|
||||
{requestHistory.length === 0 ? (
|
||||
<p className="text-sm text-gray-500 italic">No history yet</p>
|
||||
) : (
|
||||
<ul className="space-y-3">
|
||||
{requestHistory
|
||||
.slice()
|
||||
.reverse()
|
||||
.map((request, 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={() =>
|
||||
toggleRequestExpansion(requestHistory.length - 1 - index)
|
||||
}
|
||||
>
|
||||
<span className="font-mono">
|
||||
{requestHistory.length - index}.{" "}
|
||||
{JSON.parse(request.request).method}
|
||||
</span>
|
||||
<span>
|
||||
{expandedRequests[requestHistory.length - 1 - index]
|
||||
? "▼"
|
||||
: "▶"}
|
||||
</span>
|
||||
</div>
|
||||
{request.response && (
|
||||
{expandedRequests[requestHistory.length - 1 - index] && (
|
||||
<>
|
||||
<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="flex justify-between items-center mb-1">
|
||||
<span className="font-semibold text-green-600">
|
||||
Response:
|
||||
<span className="font-semibold text-purple-600">
|
||||
Details:
|
||||
</span>
|
||||
<button
|
||||
onClick={() => copyToClipboard(request.response!)}
|
||||
onClick={() =>
|
||||
copyToClipboard(JSON.stringify(notification))
|
||||
}
|
||||
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 className="whitespace-pre-wrap break-words bg-purple-50 p-2 rounded">
|
||||
{JSON.stringify(notification, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default History;
|
||||
export default HistoryAndNotifications;
|
||||
|
||||
@@ -7,6 +7,7 @@ type ListPaneProps<T> = {
|
||||
renderItem: (item: T) => React.ReactNode;
|
||||
title: string;
|
||||
buttonText: string;
|
||||
isButtonDisabled?: boolean;
|
||||
};
|
||||
|
||||
const ListPane = <T extends object>({
|
||||
@@ -16,16 +17,22 @@ const ListPane = <T extends object>({
|
||||
renderItem,
|
||||
title,
|
||||
buttonText,
|
||||
isButtonDisabled,
|
||||
}: ListPaneProps<T>) => (
|
||||
<div className="bg-white rounded-lg shadow">
|
||||
<div className="p-4 border-b border-gray-200">
|
||||
<h3 className="font-semibold">{title}</h3>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<Button variant="outline" className="w-full mb-4" onClick={listItems}>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full mb-4"
|
||||
onClick={listItems}
|
||||
disabled={isButtonDisabled}
|
||||
>
|
||||
{buttonText}
|
||||
</Button>
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-2 overflow-y-auto max-h-96">
|
||||
{items.map((item, index) => (
|
||||
<div
|
||||
key={index}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
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;
|
||||
21
client/src/components/PingTab.tsx
Normal file
21
client/src/components/PingTab.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
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;
|
||||
@@ -1,11 +1,12 @@
|
||||
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 { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { useState } from "react";
|
||||
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 { useState } from "react";
|
||||
import ListPane from "./ListPane";
|
||||
|
||||
export type Prompt = {
|
||||
@@ -25,6 +26,7 @@ const PromptsTab = ({
|
||||
selectedPrompt,
|
||||
setSelectedPrompt,
|
||||
promptContent,
|
||||
nextCursor,
|
||||
error,
|
||||
}: {
|
||||
prompts: Prompt[];
|
||||
@@ -33,6 +35,7 @@ const PromptsTab = ({
|
||||
selectedPrompt: Prompt | null;
|
||||
setSelectedPrompt: (prompt: Prompt) => void;
|
||||
promptContent: string;
|
||||
nextCursor: ListPromptsResult["nextCursor"];
|
||||
error: string | null;
|
||||
}) => {
|
||||
const [promptArgs, setPromptArgs] = useState<Record<string, string>>({});
|
||||
@@ -63,7 +66,8 @@ const PromptsTab = ({
|
||||
</>
|
||||
)}
|
||||
title="Prompts"
|
||||
buttonText="List Prompts"
|
||||
buttonText={nextCursor ? "List More Prompts" : "List Prompts"}
|
||||
isButtonDisabled={!nextCursor && prompts.length > 0}
|
||||
/>
|
||||
|
||||
<div className="bg-white rounded-lg shadow">
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import { FileText, ChevronRight, AlertCircle, RefreshCw } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { TabsContent } from "@/components/ui/tabs";
|
||||
import { ListResourcesResult, Resource } from "@modelcontextprotocol/sdk/types.js";
|
||||
import { AlertCircle, ChevronRight, FileText, RefreshCw } from "lucide-react";
|
||||
import ListPane from "./ListPane";
|
||||
|
||||
export type Resource = {
|
||||
uri: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
const ResourcesTab = ({
|
||||
resources,
|
||||
listResources,
|
||||
@@ -16,6 +12,7 @@ const ResourcesTab = ({
|
||||
selectedResource,
|
||||
setSelectedResource,
|
||||
resourceContent,
|
||||
nextCursor,
|
||||
error,
|
||||
}: {
|
||||
resources: Resource[];
|
||||
@@ -24,6 +21,7 @@ const ResourcesTab = ({
|
||||
selectedResource: Resource | null;
|
||||
setSelectedResource: (resource: Resource) => void;
|
||||
resourceContent: string;
|
||||
nextCursor: ListResourcesResult["nextCursor"];
|
||||
error: string | null;
|
||||
}) => (
|
||||
<TabsContent value="resources" className="grid grid-cols-2 gap-4">
|
||||
@@ -37,14 +35,15 @@ const ResourcesTab = ({
|
||||
renderItem={(resource) => (
|
||||
<div className="flex items-center w-full">
|
||||
<FileText className="w-4 h-4 mr-2 flex-shrink-0 text-gray-500" />
|
||||
<span className="flex-1 truncate" title={resource.uri}>
|
||||
<span className="flex-1 truncate" title={resource.uri.toString()}>
|
||||
{resource.name}
|
||||
</span>
|
||||
<ChevronRight className="w-4 h-4 flex-shrink-0 text-gray-400" />
|
||||
</div>
|
||||
)}
|
||||
title="Resources"
|
||||
buttonText="List Resources"
|
||||
buttonText={nextCursor ? "List More Resources" : "List Resources"}
|
||||
isButtonDisabled={!nextCursor && resources.length > 0}
|
||||
/>
|
||||
|
||||
<div className="bg-white rounded-lg shadow">
|
||||
|
||||
65
client/src/components/SamplingTab.tsx
Normal file
65
client/src/components/SamplingTab.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
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;
|
||||
@@ -1,11 +1,11 @@
|
||||
import { TabsContent } from "@/components/ui/tabs";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Send, AlertCircle } from "lucide-react";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import { Tool } from "mcp-typescript/types.js";
|
||||
import { useState } from "react";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { TabsContent } from "@/components/ui/tabs";
|
||||
import { ListToolsResult, Tool } from "@modelcontextprotocol/sdk/types.js";
|
||||
import { AlertCircle, Send } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import ListPane from "./ListPane";
|
||||
|
||||
const ToolsTab = ({
|
||||
@@ -15,6 +15,7 @@ const ToolsTab = ({
|
||||
selectedTool,
|
||||
setSelectedTool,
|
||||
toolResult,
|
||||
nextCursor,
|
||||
error,
|
||||
}: {
|
||||
tools: Tool[];
|
||||
@@ -23,6 +24,7 @@ const ToolsTab = ({
|
||||
selectedTool: Tool | null;
|
||||
setSelectedTool: (tool: Tool) => void;
|
||||
toolResult: string;
|
||||
nextCursor: ListToolsResult["nextCursor"];
|
||||
error: string | null;
|
||||
}) => {
|
||||
const [params, setParams] = useState<Record<string, unknown>>({});
|
||||
@@ -36,11 +38,14 @@ const ToolsTab = ({
|
||||
renderItem={(tool) => (
|
||||
<>
|
||||
<span className="flex-1">{tool.name}</span>
|
||||
<span className="text-sm text-gray-500">{tool.description}</span>
|
||||
<span className="text-sm text-gray-500 text-right">
|
||||
{tool.description}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
title="Tools"
|
||||
buttonText="List Tools"
|
||||
buttonText={nextCursor ? "List More Tools" : "List Tools"}
|
||||
isButtonDisabled={!nextCursor && tools.length > 0}
|
||||
/>
|
||||
|
||||
<div className="bg-white rounded-lg shadow">
|
||||
@@ -61,7 +66,7 @@ const ToolsTab = ({
|
||||
<p className="text-sm text-gray-600">
|
||||
{selectedTool.description}
|
||||
</p>
|
||||
{Object.entries(selectedTool.inputSchema.properties).map(
|
||||
{Object.entries(selectedTool.inputSchema.properties ?? []).map(
|
||||
([key, value]) => (
|
||||
<div key={key}>
|
||||
<Label
|
||||
@@ -71,14 +76,17 @@ const ToolsTab = ({
|
||||
{key}
|
||||
</Label>
|
||||
<Input
|
||||
// @ts-expect-error value type is currently unknown
|
||||
type={value.type === "number" ? "number" : "text"}
|
||||
id={key}
|
||||
name={key}
|
||||
// @ts-expect-error value type is currently unknown
|
||||
placeholder={value.description}
|
||||
onChange={(e) =>
|
||||
setParams({
|
||||
...params,
|
||||
[key]:
|
||||
// @ts-expect-error value type is currently unknown
|
||||
value.type === "number"
|
||||
? Number(e.target.value)
|
||||
: e.target.value,
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{"root":["./vite.config.ts"],"version":"5.6.2"}
|
||||
5815
package-lock.json
generated
Normal file
5815
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
25
package.json
25
package.json
@@ -3,20 +3,19 @@
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"workspaces": ["client", "server"],
|
||||
"workspaces": [
|
||||
"client",
|
||||
"server"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "concurrently \"cd client && yarn dev\" \"cd server && yarn dev\"",
|
||||
"build-server": "cd server && yarn build",
|
||||
"build-client": "cd client && yarn build",
|
||||
"build": "yarn build-server && yarn build-client",
|
||||
"start-server": "cd server && yarn start",
|
||||
"start-client": "cd client && yarn preview",
|
||||
"start": "concurrently \"yarn start-server\" \"yarn start-client\"",
|
||||
"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"
|
||||
"dev": "concurrently \"cd client && npm run dev\" \"cd server && npm run dev\"",
|
||||
"build-server": "cd server && npm run build",
|
||||
"build-client": "cd client && npm run build",
|
||||
"build": "npm run build-server && npm run build-client",
|
||||
"start-server": "cd server && npm run start",
|
||||
"start-client": "cd client && npm run preview",
|
||||
"start": "concurrently \"npm run start-server\" \"npm run start-client\"",
|
||||
"prettier-fix": "prettier --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"concurrently": "^9.0.1",
|
||||
|
||||
1
packages/mcp-typescript/.gitattributes
vendored
1
packages/mcp-typescript/.gitattributes
vendored
@@ -1 +0,0 @@
|
||||
dist/**/* linguist-generated=true
|
||||
@@ -1,26 +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: 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
vendored
131
packages/mcp-typescript/.gitignore
vendored
@@ -1,131 +0,0 @@
|
||||
# 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
|
||||
@@ -1,2 +0,0 @@
|
||||
# mcp-typescript
|
||||
TypeScript implementation of the Model Context Protocol
|
||||
2
packages/mcp-typescript/dist/cli.d.ts
vendored
2
packages/mcp-typescript/dist/cli.d.ts
vendored
@@ -1,2 +0,0 @@
|
||||
export {};
|
||||
//# sourceMappingURL=cli.d.ts.map
|
||||
1
packages/mcp-typescript/dist/cli.d.ts.map
vendored
1
packages/mcp-typescript/dist/cli.d.ts.map
vendored
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
||||
119
packages/mcp-typescript/dist/cli.js
vendored
119
packages/mcp-typescript/dist/cli.js
vendored
@@ -1,119 +0,0 @@
|
||||
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
vendored
1
packages/mcp-typescript/dist/cli.js.map
vendored
@@ -1 +0,0 @@
|
||||
{"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
vendored
27
packages/mcp-typescript/dist/client/index.d.ts
vendored
@@ -1,27 +0,0 @@
|
||||
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 +0,0 @@
|
||||
{"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
vendored
51
packages/mcp-typescript/dist/client/index.js
vendored
@@ -1,51 +0,0 @@
|
||||
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 +0,0 @@
|
||||
{"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
vendored
20
packages/mcp-typescript/dist/client/sse.d.ts
vendored
@@ -1,20 +0,0 @@
|
||||
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 +0,0 @@
|
||||
{"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
vendored
85
packages/mcp-typescript/dist/client/sse.js
vendored
@@ -1,85 +0,0 @@
|
||||
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 +0,0 @@
|
||||
{"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
vendored
39
packages/mcp-typescript/dist/client/stdio.d.ts
vendored
@@ -1,39 +0,0 @@
|
||||
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 +0,0 @@
|
||||
{"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
vendored
93
packages/mcp-typescript/dist/client/stdio.js
vendored
@@ -1,93 +0,0 @@
|
||||
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 +0,0 @@
|
||||
{"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/client/stdio.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,2 +0,0 @@
|
||||
export {};
|
||||
//# sourceMappingURL=stdio.test.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"stdio.test.d.ts","sourceRoot":"","sources":["../../src/client/stdio.test.ts"],"names":[],"mappings":""}
|
||||
@@ -1,51 +0,0 @@
|
||||
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
|
||||
@@ -1 +0,0 @@
|
||||
{"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"}
|
||||
@@ -1,15 +0,0 @@
|
||||
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
|
||||
@@ -1 +0,0 @@
|
||||
{"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
vendored
55
packages/mcp-typescript/dist/client/websocket.js
vendored
@@ -1,55 +0,0 @@
|
||||
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 +0,0 @@
|
||||
{"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
vendored
30
packages/mcp-typescript/dist/server/index.d.ts
vendored
@@ -1,30 +0,0 @@
|
||||
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 +0,0 @@
|
||||
{"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
vendored
43
packages/mcp-typescript/dist/server/index.js
vendored
@@ -1,43 +0,0 @@
|
||||
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 +0,0 @@
|
||||
{"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
vendored
45
packages/mcp-typescript/dist/server/sse.d.ts
vendored
@@ -1,45 +0,0 @@
|
||||
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 +0,0 @@
|
||||
{"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
vendored
115
packages/mcp-typescript/dist/server/sse.js
vendored
@@ -1,115 +0,0 @@
|
||||
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 +0,0 @@
|
||||
{"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
vendored
27
packages/mcp-typescript/dist/server/stdio.d.ts
vendored
@@ -1,27 +0,0 @@
|
||||
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 +0,0 @@
|
||||
{"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
vendored
64
packages/mcp-typescript/dist/server/stdio.js
vendored
@@ -1,64 +0,0 @@
|
||||
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 +0,0 @@
|
||||
{"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"}
|
||||
@@ -1,2 +0,0 @@
|
||||
export {};
|
||||
//# sourceMappingURL=stdio.test.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"stdio.test.d.ts","sourceRoot":"","sources":["../../src/server/stdio.test.ts"],"names":[],"mappings":""}
|
||||
@@ -1,87 +0,0 @@
|
||||
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
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"stdio.test.js","sourceRoot":"","sources":["../../src/server/stdio.test.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,92 +0,0 @@
|
||||
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
|
||||
@@ -1 +0,0 @@
|
||||
{"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
vendored
224
packages/mcp-typescript/dist/shared/protocol.js
vendored
@@ -1,224 +0,0 @@
|
||||
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
|
||||
File diff suppressed because one or more lines are too long
13
packages/mcp-typescript/dist/shared/stdio.d.ts
vendored
13
packages/mcp-typescript/dist/shared/stdio.d.ts
vendored
@@ -1,13 +0,0 @@
|
||||
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 +0,0 @@
|
||||
{"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
vendored
31
packages/mcp-typescript/dist/shared/stdio.js
vendored
@@ -1,31 +0,0 @@
|
||||
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 +0,0 @@
|
||||
{"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"}
|
||||
@@ -1,2 +0,0 @@
|
||||
export {};
|
||||
//# sourceMappingURL=stdio.test.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"stdio.test.d.ts","sourceRoot":"","sources":["../../src/shared/stdio.test.ts"],"names":[],"mappings":""}
|
||||
@@ -1,27 +0,0 @@
|
||||
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
|
||||
@@ -1 +0,0 @@
|
||||
{"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"}
|
||||
@@ -1,31 +0,0 @@
|
||||
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
|
||||
@@ -1 +0,0 @@
|
||||
{"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"}
|
||||
@@ -1,2 +0,0 @@
|
||||
export {};
|
||||
//# sourceMappingURL=transport.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/shared/transport.ts"],"names":[],"mappings":""}
|
||||
8082
packages/mcp-typescript/dist/types.d.ts
vendored
8082
packages/mcp-typescript/dist/types.d.ts
vendored
File diff suppressed because it is too large
Load Diff
1
packages/mcp-typescript/dist/types.d.ts.map
vendored
1
packages/mcp-typescript/dist/types.d.ts.map
vendored
File diff suppressed because one or more lines are too long
801
packages/mcp-typescript/dist/types.js
vendored
801
packages/mcp-typescript/dist/types.js
vendored
@@ -1,801 +0,0 @@
|
||||
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
vendored
1
packages/mcp-typescript/dist/types.js.map
vendored
File diff suppressed because one or more lines are too long
@@ -1,19 +0,0 @@
|
||||
// @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": "^_" }
|
||||
]
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -1,12 +0,0 @@
|
||||
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/"],
|
||||
};
|
||||
@@ -1,57 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
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();
|
||||
});
|
||||
@@ -1,121 +0,0 @@
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
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();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
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);
|
||||
});
|
||||
@@ -1,73 +0,0 @@
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,361 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
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);
|
||||
});
|
||||
@@ -1,39 +0,0 @@
|
||||
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
Reference in New Issue
Block a user