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 .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "12.7.0"
".": "12.8.0"
}
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# Changelog

## 12.8.0 (2025-09-18)

Full Changelog: [v12.7.0...v12.8.0](https://github.com/muxinc/mux-node-sdk/compare/v12.7.0...v12.8.0)

### Features

* **mcp:** allow setting logging level ([cf8ef79](https://github.com/muxinc/mux-node-sdk/commit/cf8ef795cd13ac0505f84f87dcc98698c26d6e5b))
* **mcp:** expose client options in `streamableHTTPApp` ([2321073](https://github.com/muxinc/mux-node-sdk/commit/23210734b3ea348d6d8e18fa43e050928a743912))


### Bug Fixes

* **mcp:** avoid importing unsupported libraries on non-node environments ([6e3db76](https://github.com/muxinc/mux-node-sdk/commit/6e3db76730719347ad239075c7293a989d8571d5))


### Chores

* add server.json to publish to Github's MCP registry ([#613](https://github.com/muxinc/mux-node-sdk/issues/613)) ([fd07791](https://github.com/muxinc/mux-node-sdk/commit/fd0779160400d828c16c1dca6f0db1eb30c06e69))

## 12.7.0 (2025-08-27)

Full Changelog: [v12.6.1...v12.7.0](https://github.com/muxinc/mux-node-sdk/compare/v12.6.1...v12.7.0)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mux/mux-node",
"version": "12.7.0",
"version": "12.8.0",
"description": "The official TypeScript library for the Mux API",
"author": "Mux <[email protected]>",
"types": "dist/index.d.ts",
Expand Down
3 changes: 2 additions & 1 deletion packages/mcp-server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mux/mcp",
"version": "12.7.0",
"version": "12.8.0",
"description": "The official MCP Server for the Mux API",
"author": "Mux <[email protected]>",
"types": "dist/index.d.ts",
Expand All @@ -14,6 +14,7 @@
"homepage": "https://github.com/muxinc/mux-node-sdk/tree/main/packages/mcp-server#readme",
"license": "Apache-2.0",
"packageManager": "[email protected]",
"mcpName": "com.mux/mcp",
"private": false,
"publishConfig": {
"access": "public"
Expand Down
67 changes: 67 additions & 0 deletions packages/mcp-server/server.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
"name": "com.mux/mcp",
"description": "The official MCP Server for the Mux API",
"status": "active",
"repository": {
"url": "https://github.com/muxinc/mux-node-sdk",
"source": "github",
"subfolder": "packages/mcp-server"
},
"version": "12.8.0",
"packages": [
{
"registry_type": "npm",
"registry_base_url": "https://registry.npmjs.org",
"identifier": "@mux/mcp",
"version": "12.8.0",
"transport": {
"type": "stdio"
},
"environment_variables": [
{
"description": "Your Mux access token ID",
"is_required": true,
"format": "string",
"is_secret": true,
"name": "MUX_TOKEN_ID"
},
{
"description": "Your Mux access token secret",
"is_required": true,
"format": "string",
"is_secret": true,
"name": "MUX_TOKEN_SECRET"
},
{
"description": "Your JWT signing key ID, for use with signed playback IDs",
"is_required": false,
"format": "string",
"is_secret": true,
"name": "MUX_SIGNING_KEY"
},
{
"description": "Your JWT private key, for use with signed playback IDs",
"is_required": false,
"format": "string",
"is_secret": true,
"name": "MUX_PRIVATE_KEY"
}
]
}
],
"remotes": [
{
"type": "streamable-http",
"url": "https://mcp.mux.com",
"headers": [
{
"name": "Authorization",
"description": "Optional basic authorization header you can include, combining your Access Token and Secret using HTTP Basic Auth. If not provided, authorization will be handled via OAuth.",
"is_required": false,
"is_secret": true
}
]
}
]
}
8 changes: 5 additions & 3 deletions packages/mcp-server/src/code-tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import { Endpoint, ContentBlock, Metadata } from './tools/types';

import { Tool } from '@modelcontextprotocol/sdk/types.js';

import { newDenoHTTPWorker } from '@valtown/deno-http-worker';
import { WorkerInput, WorkerError, WorkerSuccess } from './code-tool-types';
import { workerPath } from './code-tool-paths.cjs';

/**
* A tool that runs code against a copy of the SDK.
Expand All @@ -20,7 +18,7 @@ import { workerPath } from './code-tool-paths.cjs';
*
* @param endpoints - The endpoints to include in the list.
*/
export function codeTool(): Endpoint {
export async function codeTool(): Promise<Endpoint> {
const metadata: Metadata = { resource: 'all', operation: 'write', tags: [] };
const tool: Tool = {
name: 'execute',
Expand All @@ -29,6 +27,10 @@ export function codeTool(): Endpoint {
inputSchema: { type: 'object', properties: { code: { type: 'string' } } },
};

// Import dynamically to avoid failing at import time in cases where the environment is not well-supported.
const { newDenoHTTPWorker } = await import('@valtown/deno-http-worker');
const { workerPath } = await import('./code-tool-paths.cjs');

const handler = async (client: Mux, args: unknown) => {
const baseURLHostname = new URL(client.baseURL).hostname;
const { code } = args as { code: string };
Expand Down
58 changes: 35 additions & 23 deletions packages/mcp-server/src/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,25 @@ import cors from 'cors';
import express from 'express';
import { fromError } from 'zod-validation-error/v3';
import { McpOptions, parseQueryOptions } from './options';
import { initMcpServer, newMcpServer } from './server';
import { ClientOptions, initMcpServer, newMcpServer } from './server';
import { parseAuthHeaders } from './headers';

const oauthResourceIdentifier = (req: express.Request): string => {
const protocol = req.headers['x-forwarded-proto'] ?? req.protocol;
return `${protocol}://${req.get('host')}/`;
};

const newServer = (
defaultMcpOptions: McpOptions,
req: express.Request,
res: express.Response,
): McpServer | null => {
const newServer = ({
clientOptions,
mcpOptions: defaultMcpOptions,
req,
res,
}: {
clientOptions: ClientOptions;
mcpOptions: McpOptions;
req: express.Request;
res: express.Response;
}): McpServer | null => {
const server = newMcpServer();

let mcpOptions: McpOptions;
Expand All @@ -41,10 +47,8 @@ const newServer = (
initMcpServer({
server: server,
clientOptions: {
...clientOptions,
...authOptions,
defaultHeaders: {
'X-Stainless-MCP': 'true',
},
},
mcpOptions,
});
Expand All @@ -67,17 +71,19 @@ const newServer = (
return server;
};

const post = (defaultOptions: McpOptions) => async (req: express.Request, res: express.Response) => {
const server = newServer(defaultOptions, req, res);
// If we return null, we already set the authorization error.
if (server === null) return;
const transport = new StreamableHTTPServerTransport({
// Stateless server
sessionIdGenerator: undefined,
});
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
};
const post =
(options: { clientOptions: ClientOptions; mcpOptions: McpOptions }) =>
async (req: express.Request, res: express.Response) => {
const server = newServer({ ...options, req, res });
// If we return null, we already set the authorization error.
if (server === null) return;
const transport = new StreamableHTTPServerTransport({
// Stateless server
sessionIdGenerator: undefined,
});
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
};

const get = async (req: express.Request, res: express.Response) => {
if (req.headers['sec-fetch-dest'] === 'document') {
Expand Down Expand Up @@ -117,22 +123,28 @@ const oauthAuthorizationServer = (req: express.Request, res: express.Response) =
res.redirect('https://auth.mux.com/.well-known/oauth-authorization-server');
};

export const streamableHTTPApp = (options: McpOptions): express.Express => {
export const streamableHTTPApp = ({
clientOptions = {},
mcpOptions = {},
}: {
clientOptions?: ClientOptions;
mcpOptions?: McpOptions;
}): express.Express => {
const app = express();
app.set('query parser', 'extended');
app.use(express.json());

app.get('/.well-known/oauth-authorization-server', cors(), oauthAuthorizationServer);
app.get('/.well-known/oauth-protected-resource', cors(), oauthMetadata);
app.get('/', get);
app.post('/', cors(), post(options));
app.post('/', cors(), post({ clientOptions, mcpOptions }));
app.delete('/', del);

return app;
};

export const launchStreamableHTTPServer = async (options: McpOptions, port: number | string | undefined) => {
const app = streamableHTTPApp(options);
const app = streamableHTTPApp({ mcpOptions: options });
const server = app.listen(port);
const address = server.address();

Expand Down
6 changes: 3 additions & 3 deletions packages/mcp-server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ async function main() {
return;
}

const selectedTools = selectToolsOrError(endpoints, options);
const selectedTools = await selectToolsOrError(endpoints, options);

console.error(
`MCP Server starting with ${selectedTools.length} tools:`,
Expand Down Expand Up @@ -47,9 +47,9 @@ function parseOptionsOrError() {
}
}

function selectToolsOrError(endpoints: Endpoint[], options: McpOptions): Endpoint[] {
async function selectToolsOrError(endpoints: Endpoint[], options: McpOptions): Promise<Endpoint[]> {
try {
const includedTools = selectTools(endpoints, options);
const includedTools = await selectTools(endpoints, options);
if (includedTools.length === 0) {
console.error('No tools match the provided filters.');
process.exit(1);
Expand Down
18 changes: 9 additions & 9 deletions packages/mcp-server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { Endpoint, endpoints, HandlerFunction, query } from './tools';
import {
CallToolRequestSchema,
Implementation,
ListToolsRequestSchema,
Implementation,
Tool,
} from '@modelcontextprotocol/sdk/types.js';
import { ClientOptions } from '@mux/mux-node';
Expand All @@ -32,7 +32,7 @@ export const newMcpServer = () =>
new McpServer(
{
name: 'mux',
version: '12.7.0',
version: '12.8.0',
},
{ capabilities: { tools: {}, logging: {} } },
);
Expand All @@ -55,7 +55,7 @@ export function initMcpServer(params: {
let providedEndpoints: Endpoint[] | null = null;
let endpointMap: Record<string, Endpoint> | null = null;

const initTools = (implementation?: Implementation) => {
const initTools = async (implementation?: Implementation) => {
if (implementation && (!mcpOptions.client || mcpOptions.client === 'infer')) {
mcpOptions.client =
implementation.name.toLowerCase().includes('claude') ? 'claude'
Expand All @@ -66,8 +66,8 @@ export function initMcpServer(params: {
...mcpOptions.capabilities,
};
}
providedEndpoints = selectTools(endpoints, mcpOptions);
endpointMap = Object.fromEntries(providedEndpoints.map((endpoint) => [endpoint.tool.name, endpoint]));
providedEndpoints ??= await selectTools(endpoints, mcpOptions);
endpointMap ??= Object.fromEntries(providedEndpoints.map((endpoint) => [endpoint.tool.name, endpoint]));
};

const client = new Mux({
Expand All @@ -80,7 +80,7 @@ export function initMcpServer(params: {

server.setRequestHandler(ListToolsRequestSchema, async () => {
if (providedEndpoints === null) {
initTools(server.getClientVersion());
await initTools(server.getClientVersion());
}
return {
tools: providedEndpoints!.map((endpoint) => endpoint.tool),
Expand All @@ -89,7 +89,7 @@ export function initMcpServer(params: {

server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (endpointMap === null) {
initTools(server.getClientVersion());
await initTools(server.getClientVersion());
}
const { name, arguments: args } = request.params;
const endpoint = endpointMap![name];
Expand All @@ -104,7 +104,7 @@ export function initMcpServer(params: {
/**
* Selects the tools to include in the MCP Server based on the provided options.
*/
export function selectTools(endpoints: Endpoint[], options?: McpOptions): Endpoint[] {
export async function selectTools(endpoints: Endpoint[], options?: McpOptions): Promise<Endpoint[]> {
const filteredEndpoints = query(options?.filters ?? [], endpoints);

let includedTools = filteredEndpoints;
Expand All @@ -119,7 +119,7 @@ export function selectTools(endpoints: Endpoint[], options?: McpOptions): Endpoi
} else if (options?.includeAllTools) {
includedTools = endpoints;
} else if (options?.includeCodeTools) {
includedTools = [codeTool()];
includedTools = [await codeTool()];
} else {
includedTools = endpoints;
}
Expand Down
2 changes: 1 addition & 1 deletion src/version.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const VERSION = '12.7.0'; // x-release-please-version
export const VERSION = '12.8.0'; // x-release-please-version