Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .changeset/brave-lions-glow.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
"@modelcontextprotocol/node": patch
'@modelcontextprotocol/node': patch
---

Prevent Hono from overriding global Response object by passing `overrideGlobalObjects: false` to `getRequestListener()`. This fixes compatibility with frameworks like Next.js whose response classes extend the native Response.
28 changes: 18 additions & 10 deletions .github/pages/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
</head>
<body>
<h1>MCP TypeScript SDK</h1>

<div class="redirect-notice">
<p>Redirecting to <a href="{{ site.data.latest_version }}/">latest documentation ({{ site.data.latest_version }})</a>...</p>
</div>
Expand All @@ -142,31 +142,39 @@ <h2>All Documentation Versions</h2>

<script>
// Redirect to latest version
setTimeout(function() {
setTimeout(function () {
window.location.href = '{{ site.data.latest_version }}/';
}, 1000);

// Load and display package versions from versions.json
fetch('{{ site.data.latest_version }}/versions.json')
.then(function(response) {
.then(function (response) {
if (!response.ok) throw new Error('versions.json not found');
return response.json();
})
.then(function(data) {
.then(function (data) {
var container = document.getElementById('package-versions');
var html = '<p>Packages included in the latest documentation:</p><ul class="package-list">';

for (var pkg in data.packages) {
html += '<li><span class="package-name">' + pkg + '</span>: <span class="package-version">' + data.packages[pkg] + '</span></li>';
html +=
'<li><span class="package-name">' +
pkg +
'</span>: <span class="package-version">' +
data.packages[pkg] +
'</span></li>';
}

html += '</ul>';
html += '<p style="font-size: 0.85rem; color: #666; margin-top: 1rem;">Generated from tag: ' + data.generated_from_tag + '</p>';

html +=
'<p style="font-size: 0.85rem; color: #666; margin-top: 1rem;">Generated from tag: ' +
data.generated_from_tag +
'</p>';

container.innerHTML = html;
container.classList.add('loaded');
})
.catch(function(err) {
.catch(function (err) {
// versions.json may not exist for older docs
document.getElementById('package-versions').style.display = 'none';
});
Expand Down
7 changes: 7 additions & 0 deletions common/eslint-config/eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ export default defineConfig(
]
}
},
{
// Disable consistent-function-scoping in test files where helper functions are common
files: ['**/*.test.ts', '**/*.test.tsx', '**/test/**'],
rules: {
'unicorn/consistent-function-scoping': 'off'
}
},
{
// Ignore generated protocol types everywhere
ignores: ['**/spec.types.ts']
Expand Down
8 changes: 4 additions & 4 deletions docs/documents.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
title: Documents
children:
- ./server.md
- ./client.md
- ./capabilities.md
- ./faq.md
- ./server.md
- ./client.md
- ./capabilities.md
- ./faq.md
---

# Documents
Expand Down
6 changes: 3 additions & 3 deletions packages/client/test/client/middleware.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1018,9 +1018,9 @@ describe('createMiddleware', () => {

if (response.headers.get('content-type')?.includes('application/json')) {
const data = (await response.json()) as Record<string, unknown>;
const transformed = { ...data, timestamp: 123456789 };
const transformed = { ...data, timestamp: 123_456_789 };

return new Response(JSON.stringify(transformed), {
return Response.json(transformed, {
status: response.status,
statusText: response.statusText,
headers: response.headers
Expand All @@ -1036,7 +1036,7 @@ describe('createMiddleware', () => {

expect(result).toEqual({
data: 'original',
timestamp: 123456789
timestamp: 123_456_789
});
});

Expand Down
59 changes: 32 additions & 27 deletions packages/client/test/client/sse.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,9 +300,9 @@ describe('SSEClientTransport', () => {

await transport.start();

const originalFetch = global.fetch;
const originalFetch = globalThis.fetch;
try {
global.fetch = vi.fn().mockResolvedValue({ ok: true });
globalThis.fetch = vi.fn().mockResolvedValue({ ok: true });

const message: JSONRPCMessage = {
jsonrpc: '2.0',
Expand All @@ -313,7 +313,7 @@ describe('SSEClientTransport', () => {

await transport.send(message);

const calledHeaders = (global.fetch as Mock).mock.calls[0]![1].headers;
const calledHeaders = (globalThis.fetch as Mock).mock.calls[0]![1].headers;
expect(calledHeaders.get('Authorization')).toBe('Bearer test-token');
expect(calledHeaders.get('X-Custom-Header')).toBe('custom-value');
expect(calledHeaders.get('content-type')).toBe('application/json');
Expand All @@ -322,10 +322,10 @@ describe('SSEClientTransport', () => {

await transport.send(message);

const updatedHeaders = (global.fetch as Mock).mock.calls[1]![1].headers;
const updatedHeaders = (globalThis.fetch as Mock).mock.calls[1]![1].headers;
expect(updatedHeaders.get('X-Custom-Header')).toBe('updated-value');
} finally {
global.fetch = originalFetch;
globalThis.fetch = originalFetch;
}
});

Expand All @@ -343,9 +343,9 @@ describe('SSEClientTransport', () => {

await transport.start();

const originalFetch = global.fetch;
const originalFetch = globalThis.fetch;
try {
global.fetch = vi.fn().mockResolvedValue({ ok: true });
globalThis.fetch = vi.fn().mockResolvedValue({ ok: true });

const message: JSONRPCMessage = {
jsonrpc: '2.0',
Expand All @@ -356,7 +356,7 @@ describe('SSEClientTransport', () => {

await transport.send(message);

const calledHeaders = (global.fetch as Mock).mock.calls[0]![1].headers;
const calledHeaders = (globalThis.fetch as Mock).mock.calls[0]![1].headers;
expect(calledHeaders.get('Authorization')).toBe('Bearer test-token');
expect(calledHeaders.get('X-Custom-Header')).toBe('custom-value');
expect(calledHeaders.get('content-type')).toBe('application/json');
Expand All @@ -365,10 +365,10 @@ describe('SSEClientTransport', () => {

await transport.send(message);

const updatedHeaders = (global.fetch as Mock).mock.calls[1]![1].headers;
const updatedHeaders = (globalThis.fetch as Mock).mock.calls[1]![1].headers;
expect(updatedHeaders.get('X-Custom-Header')).toBe('updated-value');
} finally {
global.fetch = originalFetch;
globalThis.fetch = originalFetch;
}
});

Expand All @@ -384,24 +384,24 @@ describe('SSEClientTransport', () => {

await transport.start();

const originalFetch = global.fetch;
const originalFetch = globalThis.fetch;
try {
global.fetch = vi.fn().mockResolvedValue({ ok: true });
globalThis.fetch = vi.fn().mockResolvedValue({ ok: true });

await transport.send({ jsonrpc: '2.0', id: '1', method: 'test', params: {} });

const calledHeaders = (global.fetch as Mock).mock.calls[0]![1].headers;
const calledHeaders = (globalThis.fetch as Mock).mock.calls[0]![1].headers;
expect(calledHeaders.get('Authorization')).toBe('Bearer test-token');
expect(calledHeaders.get('X-Custom-Header')).toBe('custom-value');
expect(calledHeaders.get('content-type')).toBe('application/json');
} finally {
global.fetch = originalFetch;
globalThis.fetch = originalFetch;
}
});
});

describe('auth handling', () => {
const authServerMetadataUrls = ['/.well-known/oauth-authorization-server', '/.well-known/openid-configuration'];
const authServerMetadataUrls = new Set(['/.well-known/oauth-authorization-server', '/.well-known/openid-configuration']);

let mockAuthProvider: Mocked<OAuthClientProvider>;

Expand Down Expand Up @@ -516,10 +516,10 @@ describe('SSEClientTransport', () => {
return;
}

if (req.url !== '/') {
res.writeHead(404).end();
} else {
if (req.url === '/') {
res.writeHead(401).end();
} else {
res.writeHead(404).end();
}
});

Expand Down Expand Up @@ -550,7 +550,7 @@ describe('SSEClientTransport', () => {
lastServerRequest = req;

switch (req.method) {
case 'GET':
case 'GET': {
if (req.url === '/.well-known/oauth-protected-resource') {
res.writeHead(200, {
'Content-Type': 'application/json'
Expand All @@ -576,11 +576,13 @@ describe('SSEClientTransport', () => {
res.write('event: endpoint\n');
res.write(`data: ${resourceBaseUrl.href}\n\n`);
break;
}

case 'POST':
case 'POST': {
res.writeHead(401);
res.end();
break;
}
}
});

Expand Down Expand Up @@ -658,7 +660,7 @@ describe('SSEClientTransport', () => {
authServer.close();

authServer = createServer((req, res) => {
if (req.url && authServerMetadataUrls.includes(req.url)) {
if (req.url && authServerMetadataUrls.has(req.url)) {
res.writeHead(404).end();
return;
}
Expand Down Expand Up @@ -786,7 +788,7 @@ describe('SSEClientTransport', () => {
authServer.close();

authServer = createServer((req, res) => {
if (req.url && authServerMetadataUrls.includes(req.url)) {
if (req.url && authServerMetadataUrls.has(req.url)) {
res.writeHead(404).end();
return;
}
Expand Down Expand Up @@ -849,7 +851,7 @@ describe('SSEClientTransport', () => {
}

switch (req.method) {
case 'GET':
case 'GET': {
if (req.url !== '/') {
res.writeHead(404).end();
return;
Expand All @@ -863,6 +865,7 @@ describe('SSEClientTransport', () => {
res.write('event: endpoint\n');
res.write(`data: ${resourceBaseUrl.href}\n\n`);
break;
}

case 'POST': {
if (req.url !== '/') {
Expand Down Expand Up @@ -937,7 +940,7 @@ describe('SSEClientTransport', () => {
authServer.close();

authServer = createServer((req, res) => {
if (req.url && authServerMetadataUrls.includes(req.url)) {
if (req.url && authServerMetadataUrls.has(req.url)) {
res.writeHead(404).end();
return;
}
Expand Down Expand Up @@ -1309,7 +1312,7 @@ describe('SSEClientTransport', () => {
});

// Spy on global fetch to detect unauthorized usage
globalFetchSpy = vi.spyOn(global, 'fetch');
globalFetchSpy = vi.spyOn(globalThis, 'fetch');

// Create mock auth provider with default configuration
mockAuthProvider = createMockAuthProvider({
Expand Down Expand Up @@ -1386,7 +1389,7 @@ describe('SSEClientTransport', () => {
// Set up resource server that accepts SSE connection but returns 401 on POST
resourceServerHandler.mockImplementation((req: IncomingMessage, res: ServerResponse) => {
switch (req.method) {
case 'GET':
case 'GET': {
if (req.url === '/') {
// Accept SSE connection
res.writeHead(200, {
Expand All @@ -1399,8 +1402,9 @@ describe('SSEClientTransport', () => {
return;
}
break;
}

case 'POST':
case 'POST': {
if (req.url === '/') {
// Return 401 to trigger auth retry
res.writeHead(401, {
Expand All @@ -1410,6 +1414,7 @@ describe('SSEClientTransport', () => {
return;
}
break;
}
}

res.writeHead(404).end();
Expand Down
Loading
Loading