Transistor MCP Server
by gxjansen
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
import { TransistorApiClient } from "./api-client.js";
import {
isListShowsArgs,
isListEpisodesArgs,
isCreateEpisodeArgs,
isUpdateEpisodeArgs,
isGetAnalyticsArgs,
isGetEpisodeArgs,
isGetAllEpisodeAnalyticsArgs,
isListWebhooksArgs,
isSubscribeWebhookArgs,
isUnsubscribeWebhookArgs,
isGetAuthenticatedUserArgs,
isAuthorizeUploadArgs,
AuthorizeUploadArgs,
} from "./types.js";
import axios from "axios";
export class ToolHandlers {
constructor(private apiClient: TransistorApiClient) {}
getToolDefinitions() {
return [
{
name: "get_authenticated_user",
description: "Get details of the authenticated user account",
inputSchema: {
type: "object",
properties: {}, // No parameters needed
},
},
{
name: "authorize_upload",
description: "Get a pre-signed URL for uploading an audio file",
inputSchema: {
type: "object",
properties: {
filename: {
type: "string",
description: "Filename of the audio file to upload",
},
},
required: ["filename"],
},
},
{
name: "list_shows",
description: "List all shows in your Transistor.fm account",
inputSchema: {
type: "object",
properties: {
page: {
type: "number",
description: "Page number for pagination",
minimum: 1,
},
},
},
},
{
name: "list_episodes",
description: "List episodes for a specific show",
inputSchema: {
type: "object",
properties: {
show_id: {
type: "string",
description: "ID of the show to list episodes for",
},
page: {
type: "number",
description: "Page number for pagination",
minimum: 1,
},
status: {
type: "string",
enum: ["published", "draft", "scheduled"],
description: "Filter episodes by status",
},
},
required: ["show_id"],
},
},
{
name: "create_episode",
description: "Create a new episode",
inputSchema: {
type: "object",
properties: {
show_id: {
type: "string",
description: "ID of the show to create the episode in",
},
title: {
type: "string",
description: "Episode title",
},
summary: {
type: "string",
description: "Episode summary",
},
description: {
type: "string",
description: "Episode description (supports HTML)",
},
status: {
type: "string",
enum: ["published", "draft", "scheduled"],
description: "Episode status",
},
season_number: {
type: "number",
description: "Season number",
},
episode_number: {
type: "number",
description: "Episode number",
},
audio_url: {
type: "string",
description: "URL to the episode audio file",
},
},
required: ["show_id", "title", "audio_url"],
},
},
{
name: "update_episode",
description: "Update an existing episode",
inputSchema: {
type: "object",
properties: {
episode_id: {
type: "string",
description: "ID of the episode to update",
},
title: {
type: "string",
description: "New episode title",
},
summary: {
type: "string",
description: "New episode summary",
},
description: {
type: "string",
description: "New episode description (supports HTML)",
},
status: {
type: "string",
enum: ["published", "draft", "scheduled"],
description: "New episode status",
},
season_number: {
type: "number",
description: "New season number",
},
episode_number: {
type: "number",
description: "New episode number",
},
},
required: ["episode_id"],
},
},
{
name: "get_analytics",
description: "Get analytics for a show or episode",
inputSchema: {
type: "object",
properties: {
show_id: {
type: "string",
description: "ID of the show to get analytics for",
},
episode_id: {
type: "string",
description: "ID of the episode to get analytics for (optional)",
},
start_date: {
type: "string",
description: "Start date in YYYY-MM-DD format",
},
end_date: {
type: "string",
description: "End date in YYYY-MM-DD format",
},
},
required: ["show_id", "start_date", "end_date"],
},
},
{
name: "get_episode",
description: "Get a single episode",
inputSchema: {
type: "object",
properties: {
episode_id: {
type: "string",
description: "ID of the episode to get",
},
include: {
type: "array",
items: {
type: "string",
},
description: "Include related resources",
},
fields: {
type: "object",
description: "Sparse fieldsets",
},
},
required: ["episode_id"],
},
},
{
name: "get_all_episode_analytics",
description: "Get analytics for all episodes of a show",
inputSchema: {
type: "object",
properties: {
show_id: {
type: "string",
description: "ID of the show to get analytics for",
},
start_date: {
type: "string",
description: "Start date in dd-mm-yyyy format",
},
end_date: {
type: "string",
description: "End date in dd-mm-yyyy format",
},
},
required: ["show_id", "start_date", "end_date"],
},
},
{
name: "list_webhooks",
description: "List all webhooks for a show",
inputSchema: {
type: "object",
properties: {
show_id: {
type: "string",
description: "ID of the show to list webhooks for",
},
},
required: ["show_id"],
},
},
{
name: "subscribe_webhook",
description: "Subscribe to a webhook for a show",
inputSchema: {
type: "object",
properties: {
event_name: {
type: "string",
description: "Event name (e.g., 'episode_created')",
},
show_id: {
type: "string",
description: "ID of the show to subscribe to",
},
url: {
type: "string",
description: "URL to receive webhook events",
},
},
required: ["event_name", "show_id", "url"],
},
},
{
name: "unsubscribe_webhook",
description: "Unsubscribe from a webhook",
inputSchema: {
type: "object",
properties: {
webhook_id: {
type: "string",
description: "ID of the webhook to unsubscribe from",
},
},
required: ["webhook_id"],
},
},
];
}
async handleToolCall(name: string, args: unknown) {
try {
switch (name) {
case "get_authenticated_user": {
if (!isGetAuthenticatedUserArgs(args)) {
throw new McpError(
ErrorCode.InvalidParams,
"Invalid arguments for get_authenticated_user"
);
}
const data = await this.apiClient.getAuthenticatedUser();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
case "authorize_upload": {
if (!isAuthorizeUploadArgs(args)) {
throw new McpError(
ErrorCode.InvalidParams,
"Invalid arguments for authorize_upload"
);
}
const data = await this.apiClient.authorizeUpload(args);
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
case "list_shows": {
if (!isListShowsArgs(args)) {
throw new McpError(
ErrorCode.InvalidParams,
"Invalid arguments for list_shows"
);
}
const data = await this.apiClient.listShows(args);
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
case "list_episodes": {
if (!isListEpisodesArgs(args)) {
throw new McpError(
ErrorCode.InvalidParams,
"Invalid arguments for list_episodes"
);
}
const data = await this.apiClient.listEpisodes(args);
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
case "create_episode": {
if (!isCreateEpisodeArgs(args)) {
throw new McpError(
ErrorCode.InvalidParams,
"Invalid arguments for create_episode"
);
}
const data = await this.apiClient.createEpisode(args);
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
case "update_episode": {
if (!isUpdateEpisodeArgs(args)) {
throw new McpError(
ErrorCode.InvalidParams,
"Invalid arguments for update_episode"
);
}
const data = await this.apiClient.updateEpisode(args);
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
case "get_analytics": {
if (!isGetAnalyticsArgs(args)) {
throw new McpError(
ErrorCode.InvalidParams,
"Invalid arguments for get_analytics"
);
}
const data = await this.apiClient.getAnalytics(args);
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
case "get_episode": {
if (!isGetEpisodeArgs(args)) {
throw new McpError(
ErrorCode.InvalidParams,
"Invalid arguments for get_episode"
);
}
const data = await this.apiClient.getEpisode(args);
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
case "get_all_episode_analytics": {
if (!isGetAllEpisodeAnalyticsArgs(args)) {
throw new McpError(
ErrorCode.InvalidParams,
"Invalid arguments for get_all_episode_analytics"
);
}
const data = await this.apiClient.getAllEpisodeAnalytics(args);
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
case "list_webhooks": {
if (!isListWebhooksArgs(args)) {
throw new McpError(
ErrorCode.InvalidParams,
"Invalid arguments for list_webhooks"
);
}
const data = await this.apiClient.listWebhooks(args);
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
case "subscribe_webhook": {
if (!isSubscribeWebhookArgs(args)) {
throw new McpError(
ErrorCode.InvalidParams,
"Invalid arguments for subscribe_webhook"
);
}
const data = await this.apiClient.subscribeWebhook(args);
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
case "unsubscribe_webhook": {
if (!isUnsubscribeWebhookArgs(args)) {
throw new McpError(
ErrorCode.InvalidParams,
"Invalid arguments for unsubscribe_webhook"
);
}
const data = await this.apiClient.unsubscribeWebhook(args);
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
default:
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${name}`
);
}
} catch (error) {
if (axios.isAxiosError(error)) {
return {
content: [
{
type: "text",
text: `Transistor API error: ${
error.response?.data?.message ?? error.message
}`,
},
],
isError: true,
};
}
throw error;
}
}
}