Skip to content

Commit 9ff362d

Browse files
authored
Merge pull request #19 from modelcontextprotocol/ashwin/pagination
add pagination handling for lists
2 parents 5f9d11b + 24d005a commit 9ff362d

File tree

5 files changed

+41
-10
lines changed

5 files changed

+41
-10
lines changed

client/src/App.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ const App = () => {
7474
);
7575
const [selectedPrompt, setSelectedPrompt] = useState<Prompt | null>(null);
7676
const [selectedTool, setSelectedTool] = useState<Tool | null>(null);
77+
const [nextResourceCursor, setNextResourceCursor] = useState<
78+
string | undefined
79+
>();
80+
const [nextPromptCursor, setNextPromptCursor] = useState<
81+
string | undefined
82+
>();
83+
const [nextToolCursor, setNextToolCursor] = useState<string | undefined>();
7784
const progressTokenRef = useRef(0);
7885

7986
const pushHistory = (request: object, response: object) => {
@@ -105,12 +112,12 @@ const App = () => {
105112
const response = await makeRequest(
106113
{
107114
method: "resources/list" as const,
115+
params: nextResourceCursor ? { cursor: nextResourceCursor } : {},
108116
},
109117
ListResourcesResultSchema,
110118
);
111-
if (response.resources) {
112-
setResources(response.resources);
113-
}
119+
setResources(resources.concat(response.resources ?? []));
120+
setNextResourceCursor(response.nextCursor);
114121
};
115122

116123
const readResource = async (uri: URL) => {
@@ -128,10 +135,12 @@ const App = () => {
128135
const response = await makeRequest(
129136
{
130137
method: "prompts/list" as const,
138+
params: nextPromptCursor ? { cursor: nextPromptCursor } : {},
131139
},
132140
ListPromptsResultSchema,
133141
);
134142
setPrompts(response.prompts);
143+
setNextPromptCursor(response.nextCursor);
135144
};
136145

137146
const getPrompt = async (name: string, args: Record<string, string> = {}) => {
@@ -149,10 +158,12 @@ const App = () => {
149158
const response = await makeRequest(
150159
{
151160
method: "tools/list" as const,
161+
params: nextToolCursor ? { cursor: nextToolCursor } : {},
152162
},
153163
ListToolsResultSchema,
154164
);
155165
setTools(response.tools);
166+
setNextToolCursor(response.nextCursor);
156167
};
157168

158169
const callTool = async (name: string, params: Record<string, unknown>) => {
@@ -293,6 +304,7 @@ const App = () => {
293304
selectedResource={selectedResource}
294305
setSelectedResource={setSelectedResource}
295306
resourceContent={resourceContent}
307+
nextCursor={nextResourceCursor}
296308
error={error}
297309
/>
298310
<PromptsTab
@@ -302,6 +314,7 @@ const App = () => {
302314
selectedPrompt={selectedPrompt}
303315
setSelectedPrompt={setSelectedPrompt}
304316
promptContent={promptContent}
317+
nextCursor={nextPromptCursor}
305318
error={error}
306319
/>
307320
<RequestsTab />
@@ -315,6 +328,7 @@ const App = () => {
315328
setToolResult("");
316329
}}
317330
toolResult={toolResult}
331+
nextCursor={nextToolCursor}
318332
error={error}
319333
/>
320334
<ConsoleTab />

client/src/components/ListPane.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ type ListPaneProps<T> = {
77
renderItem: (item: T) => React.ReactNode;
88
title: string;
99
buttonText: string;
10+
isButtonDisabled?: boolean;
1011
};
1112

1213
const ListPane = <T extends object>({
@@ -16,16 +17,22 @@ const ListPane = <T extends object>({
1617
renderItem,
1718
title,
1819
buttonText,
20+
isButtonDisabled,
1921
}: ListPaneProps<T>) => (
2022
<div className="bg-white rounded-lg shadow">
2123
<div className="p-4 border-b border-gray-200">
2224
<h3 className="font-semibold">{title}</h3>
2325
</div>
2426
<div className="p-4">
25-
<Button variant="outline" className="w-full mb-4" onClick={listItems}>
27+
<Button
28+
variant="outline"
29+
className="w-full mb-4"
30+
onClick={listItems}
31+
disabled={isButtonDisabled}
32+
>
2633
{buttonText}
2734
</Button>
28-
<div className="space-y-2">
35+
<div className="space-y-2 overflow-y-auto max-h-96">
2936
{items.map((item, index) => (
3037
<div
3138
key={index}

client/src/components/PromptsTab.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Textarea } from "@/components/ui/textarea";
77
import { useState } from "react";
88
import { Label } from "@/components/ui/label";
99
import ListPane from "./ListPane";
10+
import { ListPromptsResult } from "mcp-typescript/types.js";
1011

1112
export type Prompt = {
1213
name: string;
@@ -25,6 +26,7 @@ const PromptsTab = ({
2526
selectedPrompt,
2627
setSelectedPrompt,
2728
promptContent,
29+
nextCursor,
2830
error,
2931
}: {
3032
prompts: Prompt[];
@@ -33,6 +35,7 @@ const PromptsTab = ({
3335
selectedPrompt: Prompt | null;
3436
setSelectedPrompt: (prompt: Prompt) => void;
3537
promptContent: string;
38+
nextCursor: ListPromptsResult["nextCursor"];
3639
error: string | null;
3740
}) => {
3841
const [promptArgs, setPromptArgs] = useState<Record<string, string>>({});
@@ -63,7 +66,8 @@ const PromptsTab = ({
6366
</>
6467
)}
6568
title="Prompts"
66-
buttonText="List Prompts"
69+
buttonText={nextCursor ? "List More Prompts" : "List Prompts"}
70+
isButtonDisabled={!nextCursor && prompts.length > 0}
6771
/>
6872

6973
<div className="bg-white rounded-lg shadow">

client/src/components/ResourcesTab.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { FileText, ChevronRight, AlertCircle, RefreshCw } from "lucide-react";
22
import { Button } from "@/components/ui/button";
33
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
44
import { TabsContent } from "@/components/ui/tabs";
5-
import { Resource } from "mcp-typescript/types.js";
5+
import { ListResourcesResult, Resource } from "mcp-typescript/types.js";
66
import ListPane from "./ListPane";
77

88
const ResourcesTab = ({
@@ -12,6 +12,7 @@ const ResourcesTab = ({
1212
selectedResource,
1313
setSelectedResource,
1414
resourceContent,
15+
nextCursor,
1516
error,
1617
}: {
1718
resources: Resource[];
@@ -20,6 +21,7 @@ const ResourcesTab = ({
2021
selectedResource: Resource | null;
2122
setSelectedResource: (resource: Resource) => void;
2223
resourceContent: string;
24+
nextCursor: ListResourcesResult["nextCursor"];
2325
error: string | null;
2426
}) => (
2527
<TabsContent value="resources" className="grid grid-cols-2 gap-4">
@@ -40,7 +42,8 @@ const ResourcesTab = ({
4042
</div>
4143
)}
4244
title="Resources"
43-
buttonText="List Resources"
45+
buttonText={nextCursor ? "List More Resources" : "List Resources"}
46+
isButtonDisabled={!nextCursor && resources.length > 0}
4447
/>
4548

4649
<div className="bg-white rounded-lg shadow">

client/src/components/ToolsTab.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Button } from "@/components/ui/button";
33
import { Input } from "@/components/ui/input";
44
import { Send, AlertCircle } from "lucide-react";
55
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
6-
import { Tool } from "mcp-typescript/types.js";
6+
import { ListToolsResult, Tool } from "mcp-typescript/types.js";
77
import { useState } from "react";
88
import { Label } from "@/components/ui/label";
99
import ListPane from "./ListPane";
@@ -15,6 +15,7 @@ const ToolsTab = ({
1515
selectedTool,
1616
setSelectedTool,
1717
toolResult,
18+
nextCursor,
1819
error,
1920
}: {
2021
tools: Tool[];
@@ -23,6 +24,7 @@ const ToolsTab = ({
2324
selectedTool: Tool | null;
2425
setSelectedTool: (tool: Tool) => void;
2526
toolResult: string;
27+
nextCursor: ListToolsResult["nextCursor"];
2628
error: string | null;
2729
}) => {
2830
const [params, setParams] = useState<Record<string, unknown>>({});
@@ -42,7 +44,8 @@ const ToolsTab = ({
4244
</>
4345
)}
4446
title="Tools"
45-
buttonText="List Tools"
47+
buttonText={nextCursor ? "List More Tools" : "List Tools"}
48+
isButtonDisabled={!nextCursor && tools.length > 0}
4649
/>
4750

4851
<div className="bg-white rounded-lg shadow">

0 commit comments

Comments
 (0)