Skip to content

Commit 049f26e

Browse files
v2 ci: add Bun and Deno integration tests (modelcontextprotocol#1607)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 26c7242 commit 049f26e

9 files changed

Lines changed: 176 additions & 6 deletions

File tree

.github/workflows/main.yml

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,47 @@ jobs:
5858

5959
- run: pnpm test:all
6060

61+
test-runtimes:
62+
runs-on: ubuntu-latest
63+
strategy:
64+
fail-fast: false
65+
matrix:
66+
include:
67+
- runtime: bun
68+
version: "1.x"
69+
- runtime: deno
70+
version: v2.x
71+
steps:
72+
- uses: actions/checkout@v6
73+
- name: Install pnpm
74+
uses: pnpm/action-setup@v4
75+
with:
76+
run_install: false
77+
- uses: actions/setup-node@v6
78+
with:
79+
node-version: 24
80+
cache: pnpm
81+
cache-dependency-path: pnpm-lock.yaml
82+
- name: Set up Bun
83+
if: matrix.runtime == 'bun'
84+
uses: oven-sh/setup-bun@v2
85+
with:
86+
bun-version: ${{ matrix.version }}
87+
- name: Set up Deno
88+
if: matrix.runtime == 'deno'
89+
uses: denoland/setup-deno@v2
90+
with:
91+
deno-version: ${{ matrix.version }}
92+
- run: pnpm install
93+
- run: pnpm build:all
94+
- name: Run ${{ matrix.runtime }} integration tests
95+
run: pnpm --filter @modelcontextprotocol/test-integration test:integration:${{ matrix.runtime }}
96+
6197
publish:
6298
runs-on: ubuntu-latest
6399
if: github.event_name == 'release'
64100
environment: release
65-
needs: [build, test]
101+
needs: [build, test, test-runtimes]
66102

67103
permissions:
68104
contents: read

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
The Model Context Protocol (MCP) allows applications to provide context for LLMs in a standardized way, separating the concerns of providing context from the actual LLM interaction.
2727

28-
This repository contains the TypeScript SDK implementation of the MCP specification and ships:
28+
This repository contains the TypeScript SDK implementation of the MCP specification. It runs on **Node.js**, **Bun**, and **Deno**, and ships:
2929

3030
- MCP **server** libraries (tools/resources/prompts, Streamable HTTP, stdio, auth helpers)
3131
- MCP **client** libraries (transports, high-level helpers, OAuth helpers)
@@ -57,12 +57,20 @@ They are intentionally thin adapters: they should not introduce new MCP function
5757

5858
```bash
5959
npm install @modelcontextprotocol/server zod
60+
# or
61+
bun add @modelcontextprotocol/server zod
62+
# or
63+
deno add npm:@modelcontextprotocol/server npm:zod
6064
```
6165

6266
### Client
6367

6468
```bash
6569
npm install @modelcontextprotocol/client zod
70+
# or
71+
bun add @modelcontextprotocol/client zod
72+
# or
73+
deno add npm:@modelcontextprotocol/client npm:zod
6674
```
6775

6876
### Optional middleware packages

docs/client-quickstart.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@ This quickstart assumes you have familiarity with:
1919

2020
Before starting, ensure your system meets these requirements:
2121

22-
- Node.js 20 or higher installed
22+
- Node.js 20 or higher installed (or **Bun** / **Deno** — the SDK supports all three runtimes)
2323
- Latest version of `npm` installed
2424
- An Anthropic API key from the [Anthropic Console](https://console.anthropic.com/settings/keys)
2525

26+
> [!TIP]
27+
> This tutorial uses Node.js and npm, but you can substitute `bun` or `deno` commands where appropriate. For example, use `bun add` instead of `npm install`, or run the client with `bun run` / `deno run`.
28+
2629
## Set up your environment
2730

2831
First, let's create and set up our project:

docs/server-quickstart.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ node --version
3636
npm --version
3737
```
3838

39+
> [!TIP]
40+
> The MCP SDK also works with **Bun** and **Deno**. This tutorial uses Node.js, but you can substitute `bun` or `deno` commands where appropriate. For HTTP-based servers on Bun or Deno, use `WebStandardStreamableHTTPServerTransport` instead of the Node.js-specific transport — see the [server guide](./server.md) for details.
41+
3942
## Set up your environment
4043

4144
First, let's install Node.js and npm if you haven't already. You can download them from [nodejs.org](https://nodejs.org/).

test/integration/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
"test:watch": "vitest",
3030
"start": "npm run server",
3131
"server": "tsx watch --clear-screen=false scripts/cli.ts server",
32-
"client": "tsx scripts/cli.ts client"
32+
"client": "tsx scripts/cli.ts client",
33+
"test:integration:bun": "bun test test/server/bun.test.ts",
34+
"test:integration:deno": "deno test --no-check --allow-net --allow-read --allow-env test/server/deno.test.ts"
3335
},
3436
"devDependencies": {
3537
"@modelcontextprotocol/core": "workspace:^",
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* Bun integration test
3+
*
4+
* Verifies the MCP server and client packages work natively on Bun.
5+
* Run with: bun test test/server/bun.test.ts
6+
*/
7+
8+
import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client';
9+
import { McpServer, WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/server';
10+
// eslint-disable-next-line import/no-unresolved
11+
import { afterAll, beforeAll, describe, expect, it } from 'bun:test';
12+
import * as z from 'zod/v4';
13+
14+
describe('MCP on Bun', () => {
15+
let httpServer: ReturnType<typeof Bun.serve>;
16+
let transport: WebStandardStreamableHTTPServerTransport;
17+
18+
beforeAll(async () => {
19+
const mcpServer = new McpServer({ name: 'test-server', version: '1.0.0' });
20+
21+
mcpServer.registerTool(
22+
'greet',
23+
{
24+
description: 'Greet someone',
25+
inputSchema: z.object({ name: z.string() })
26+
},
27+
async ({ name }) => ({
28+
content: [{ type: 'text' as const, text: `Hello, ${name}!` }]
29+
})
30+
);
31+
32+
transport = new WebStandardStreamableHTTPServerTransport();
33+
await mcpServer.connect(transport);
34+
35+
httpServer = Bun.serve({
36+
port: 0,
37+
fetch: req => transport.handleRequest(req)
38+
});
39+
});
40+
41+
afterAll(async () => {
42+
await transport?.close();
43+
httpServer?.stop();
44+
});
45+
46+
it('should handle MCP tool calls', async () => {
47+
const client = new Client({ name: 'test-client', version: '1.0.0' });
48+
const clientTransport = new StreamableHTTPClientTransport(new URL(`http://localhost:${httpServer.port}`));
49+
50+
await client.connect(clientTransport);
51+
52+
const result = await client.callTool({ name: 'greet', arguments: { name: 'Bun' } });
53+
expect(result.content).toEqual([{ type: 'text', text: 'Hello, Bun!' }]);
54+
55+
await client.close();
56+
});
57+
});
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* Deno integration test
3+
*
4+
* Verifies the MCP server and client packages work natively on Deno.
5+
* Run with: deno test --no-check --allow-net --allow-read --allow-env test/server/deno.test.ts
6+
*/
7+
8+
import assert from 'node:assert/strict';
9+
10+
import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client';
11+
import { McpServer, WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/server';
12+
import * as z from 'zod/v4';
13+
14+
Deno.test({
15+
name: 'MCP tool calls work on Deno',
16+
sanitizeOps: false,
17+
sanitizeResources: false,
18+
async fn() {
19+
const mcpServer = new McpServer({ name: 'test-server', version: '1.0.0' });
20+
21+
mcpServer.registerTool(
22+
'greet',
23+
{
24+
description: 'Greet someone',
25+
inputSchema: z.object({ name: z.string() })
26+
},
27+
async ({ name }) => ({
28+
content: [{ type: 'text' as const, text: `Hello, ${name}!` }]
29+
})
30+
);
31+
32+
const transport = new WebStandardStreamableHTTPServerTransport();
33+
await mcpServer.connect(transport);
34+
35+
const httpServer = Deno.serve({ port: 0 }, req => transport.handleRequest(req));
36+
const port = httpServer.addr.port;
37+
38+
try {
39+
const client = new Client({ name: 'test-client', version: '1.0.0' });
40+
const clientTransport = new StreamableHTTPClientTransport(new URL(`http://localhost:${port}`));
41+
42+
await client.connect(clientTransport);
43+
44+
const result = await client.callTool({ name: 'greet', arguments: { name: 'Deno' } });
45+
assert.deepStrictEqual(result.content, [{ type: 'text', text: 'Hello, Deno!' }]);
46+
47+
await client.close();
48+
} finally {
49+
await transport.close();
50+
await httpServer.shutdown();
51+
}
52+
}
53+
});

test/integration/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"extends": "@modelcontextprotocol/tsconfig",
33
"include": ["./"],
4-
"exclude": ["node_modules", "dist"],
4+
"exclude": ["node_modules", "dist", "test/server/bun.test.ts", "test/server/deno.test.ts"],
55
"compilerOptions": {
66
"paths": {
77
"*": ["./*"],

test/integration/vitest.config.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
import { defineConfig, mergeConfig } from 'vitest/config';
12
import baseConfig from '../../common/vitest-config/vitest.config.js';
23

3-
export default baseConfig;
4+
export default mergeConfig(
5+
baseConfig,
6+
defineConfig({
7+
test: {
8+
exclude: ['**/dist/**', '**/bun.test.ts', '**/deno.test.ts']
9+
}
10+
})
11+
);

0 commit comments

Comments
 (0)