Skip to content

Commit 9b95fa8

Browse files
committed
Server.prompt() convenience API
1 parent 5ddfa0a commit 9b95fa8

File tree

2 files changed

+433
-2
lines changed

2 files changed

+433
-2
lines changed

src/server/index.test.ts

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import {
2020
ListResourcesResultSchema,
2121
ListResourceTemplatesResultSchema,
2222
ReadResourceResultSchema,
23+
ListPromptsResultSchema,
24+
GetPromptResultSchema,
2325
} from "../types.js";
2426
import { Transport } from "../shared/transport.js";
2527
import { InMemoryTransport } from "../inMemory.js";
@@ -1334,3 +1336,287 @@ describe("Server.resource", () => {
13341336
).rejects.toThrow(/Resource test:\/\/nonexistent not found/);
13351337
});
13361338
});
1339+
1340+
describe("Server.prompt", () => {
1341+
test("should register zero-argument prompt", async () => {
1342+
const server = new Server({
1343+
name: "test server",
1344+
version: "1.0",
1345+
});
1346+
const client = new Client({
1347+
name: "test client",
1348+
version: "1.0",
1349+
});
1350+
1351+
server.prompt("test", async () => ({
1352+
messages: [
1353+
{
1354+
role: "assistant",
1355+
content: {
1356+
type: "text",
1357+
text: "Test response",
1358+
},
1359+
},
1360+
],
1361+
}));
1362+
1363+
const [clientTransport, serverTransport] =
1364+
InMemoryTransport.createLinkedPair();
1365+
1366+
await Promise.all([
1367+
client.connect(clientTransport),
1368+
server.connect(serverTransport),
1369+
]);
1370+
1371+
const result = await client.request(
1372+
{
1373+
method: "prompts/list",
1374+
},
1375+
ListPromptsResultSchema,
1376+
);
1377+
1378+
expect(result.prompts).toHaveLength(1);
1379+
expect(result.prompts[0].name).toBe("test");
1380+
expect(result.prompts[0].arguments).toBeUndefined();
1381+
});
1382+
1383+
test("should register prompt with args schema", async () => {
1384+
const server = new Server({
1385+
name: "test server",
1386+
version: "1.0",
1387+
});
1388+
const client = new Client({
1389+
name: "test client",
1390+
version: "1.0",
1391+
});
1392+
1393+
server.prompt(
1394+
"test",
1395+
{
1396+
name: z.string(),
1397+
value: z.string(),
1398+
},
1399+
async ({ name, value }) => ({
1400+
messages: [
1401+
{
1402+
role: "assistant",
1403+
content: {
1404+
type: "text",
1405+
text: `${name}: ${value}`,
1406+
},
1407+
},
1408+
],
1409+
}),
1410+
);
1411+
1412+
const [clientTransport, serverTransport] =
1413+
InMemoryTransport.createLinkedPair();
1414+
1415+
await Promise.all([
1416+
client.connect(clientTransport),
1417+
server.connect(serverTransport),
1418+
]);
1419+
1420+
const result = await client.request(
1421+
{
1422+
method: "prompts/list",
1423+
},
1424+
ListPromptsResultSchema,
1425+
);
1426+
1427+
expect(result.prompts).toHaveLength(1);
1428+
expect(result.prompts[0].name).toBe("test");
1429+
expect(result.prompts[0].arguments).toEqual([
1430+
{ name: "name", required: true },
1431+
{ name: "value", required: true },
1432+
]);
1433+
});
1434+
1435+
test("should register prompt with description", async () => {
1436+
const server = new Server({
1437+
name: "test server",
1438+
version: "1.0",
1439+
});
1440+
const client = new Client({
1441+
name: "test client",
1442+
version: "1.0",
1443+
});
1444+
1445+
server.prompt("test", "Test description", async () => ({
1446+
messages: [
1447+
{
1448+
role: "assistant",
1449+
content: {
1450+
type: "text",
1451+
text: "Test response",
1452+
},
1453+
},
1454+
],
1455+
}));
1456+
1457+
const [clientTransport, serverTransport] =
1458+
InMemoryTransport.createLinkedPair();
1459+
1460+
await Promise.all([
1461+
client.connect(clientTransport),
1462+
server.connect(serverTransport),
1463+
]);
1464+
1465+
const result = await client.request(
1466+
{
1467+
method: "prompts/list",
1468+
},
1469+
ListPromptsResultSchema,
1470+
);
1471+
1472+
expect(result.prompts).toHaveLength(1);
1473+
expect(result.prompts[0].name).toBe("test");
1474+
expect(result.prompts[0].description).toBe("Test description");
1475+
});
1476+
1477+
test("should validate prompt args", async () => {
1478+
const server = new Server({
1479+
name: "test server",
1480+
version: "1.0",
1481+
});
1482+
1483+
const client = new Client(
1484+
{
1485+
name: "test client",
1486+
version: "1.0",
1487+
},
1488+
{
1489+
capabilities: {
1490+
prompts: {},
1491+
},
1492+
},
1493+
);
1494+
1495+
server.prompt(
1496+
"test",
1497+
{
1498+
name: z.string(),
1499+
value: z.string().min(3),
1500+
},
1501+
async ({ name, value }) => ({
1502+
messages: [
1503+
{
1504+
role: "assistant",
1505+
content: {
1506+
type: "text",
1507+
text: `${name}: ${value}`,
1508+
},
1509+
},
1510+
],
1511+
}),
1512+
);
1513+
1514+
const [clientTransport, serverTransport] =
1515+
InMemoryTransport.createLinkedPair();
1516+
1517+
await Promise.all([
1518+
client.connect(clientTransport),
1519+
server.connect(serverTransport),
1520+
]);
1521+
1522+
await expect(
1523+
client.request(
1524+
{
1525+
method: "prompts/get",
1526+
params: {
1527+
name: "test",
1528+
arguments: {
1529+
name: "test",
1530+
value: "ab", // Too short
1531+
},
1532+
},
1533+
},
1534+
GetPromptResultSchema,
1535+
),
1536+
).rejects.toThrow(/Invalid arguments/);
1537+
});
1538+
1539+
test("should prevent duplicate prompt registration", () => {
1540+
const server = new Server({
1541+
name: "test server",
1542+
version: "1.0",
1543+
});
1544+
1545+
server.prompt("test", async () => ({
1546+
messages: [
1547+
{
1548+
role: "assistant",
1549+
content: {
1550+
type: "text",
1551+
text: "Test response",
1552+
},
1553+
},
1554+
],
1555+
}));
1556+
1557+
expect(() => {
1558+
server.prompt("test", async () => ({
1559+
messages: [
1560+
{
1561+
role: "assistant",
1562+
content: {
1563+
type: "text",
1564+
text: "Test response 2",
1565+
},
1566+
},
1567+
],
1568+
}));
1569+
}).toThrow(/already registered/);
1570+
});
1571+
1572+
test("should throw McpError for invalid prompt name", async () => {
1573+
const server = new Server({
1574+
name: "test server",
1575+
version: "1.0",
1576+
});
1577+
1578+
const client = new Client(
1579+
{
1580+
name: "test client",
1581+
version: "1.0",
1582+
},
1583+
{
1584+
capabilities: {
1585+
prompts: {},
1586+
},
1587+
},
1588+
);
1589+
1590+
server.prompt("test-prompt", async () => ({
1591+
messages: [
1592+
{
1593+
role: "assistant",
1594+
content: {
1595+
type: "text",
1596+
text: "Test response",
1597+
},
1598+
},
1599+
],
1600+
}));
1601+
1602+
const [clientTransport, serverTransport] =
1603+
InMemoryTransport.createLinkedPair();
1604+
1605+
await Promise.all([
1606+
client.connect(clientTransport),
1607+
server.connect(serverTransport),
1608+
]);
1609+
1610+
await expect(
1611+
client.request(
1612+
{
1613+
method: "prompts/get",
1614+
params: {
1615+
name: "nonexistent-prompt",
1616+
},
1617+
},
1618+
GetPromptResultSchema,
1619+
),
1620+
).rejects.toThrow(/Prompt nonexistent-prompt not found/);
1621+
});
1622+
});

0 commit comments

Comments
 (0)