Skip to main content
Glama

get_content

Retrieve detailed metadata for Khan Academy educational content including videos, articles, and exercises by providing a slug or URL.

Instructions

Get details about a specific Khan Academy content item (video, article, or exercise). Accepts a slug or full URL. Returns title, description, type, and metadata.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
slugYesContent slug or full URL (e.g., 'math/algebra/v/intro-to-algebra', 'https://www.khanacademy.org/science/biology/a/intro-to-biology')

Implementation Reference

  • The async handler function that executes the get_content tool logic. It calls client.getContent(slug), handles not-found cases, and formats the response with title, type, URL, description, duration, YouTube link, authors, and date.
    async ({ slug }) => {
      try {
        const content = await client.getContent(slug);
    
        if (!content) {
          return {
            content: [
              {
                type: "text" as const,
                text: `Content not found for "${slug}". Check the slug/URL and try again. Use \`search\` to find content.`,
              },
            ],
          };
        }
    
        let text = `## ${content.title}\n`;
        text += `**Type:** ${content.kind}\n`;
        text += `**URL:** ${content.kaUrl}\n`;
    
        if (content.description) {
          text += `\n${content.description}\n`;
        }
    
        if (content.duration) {
          text += `\n**Duration:** ${formatDuration(content.duration)}\n`;
        }
    
        if (content.youtubeId) {
          text += `**YouTube:** https://www.youtube.com/watch?v=${content.youtubeId}\n`;
          text += `\n*Use \`get_transcript\` with this slug to get the video transcript.*\n`;
        }
    
        if (content.authorNames?.length) {
          text += `**Authors:** ${content.authorNames.join(", ")}\n`;
        }
    
        if (content.dateAdded) {
          text += `**Date Added:** ${content.dateAdded}\n`;
        }
    
        return {
          content: [{ type: "text" as const, text }],
        };
      } catch (error) {
        return {
          content: [
            {
              type: "text" as const,
              text: `Error fetching content: ${error instanceof Error ? error.message : "Unknown error"}`,
            },
          ],
          isError: true,
        };
      }
    }
  • Zod schema defining the 'slug' input parameter for get_content tool. Accepts a string that can be a content slug or full URL.
    slug: z
      .string()
      .describe(
        "Content slug or full URL (e.g., 'math/algebra/v/intro-to-algebra', 'https://www.khanacademy.org/science/biology/a/intro-to-biology')"
      ),
  • Registration of the get_content tool with the MCP server using server.tool(). Includes tool name, description, input schema, and handler function.
    server.tool(
      "get_content",
      "Get details about a specific Khan Academy content item (video, article, or exercise). Accepts a slug or full URL. Returns title, description, type, and metadata.",
      {
        slug: z
          .string()
          .describe(
            "Content slug or full URL (e.g., 'math/algebra/v/intro-to-algebra', 'https://www.khanacademy.org/science/biology/a/intro-to-biology')"
          ),
      },
      async ({ slug }) => {
        try {
          const content = await client.getContent(slug);
    
          if (!content) {
            return {
              content: [
                {
                  type: "text" as const,
                  text: `Content not found for "${slug}". Check the slug/URL and try again. Use \`search\` to find content.`,
                },
              ],
            };
          }
    
          let text = `## ${content.title}\n`;
          text += `**Type:** ${content.kind}\n`;
          text += `**URL:** ${content.kaUrl}\n`;
    
          if (content.description) {
            text += `\n${content.description}\n`;
          }
    
          if (content.duration) {
            text += `\n**Duration:** ${formatDuration(content.duration)}\n`;
          }
    
          if (content.youtubeId) {
            text += `**YouTube:** https://www.youtube.com/watch?v=${content.youtubeId}\n`;
            text += `\n*Use \`get_transcript\` with this slug to get the video transcript.*\n`;
          }
    
          if (content.authorNames?.length) {
            text += `**Authors:** ${content.authorNames.join(", ")}\n`;
          }
    
          if (content.dateAdded) {
            text += `**Date Added:** ${content.dateAdded}\n`;
          }
    
          return {
            content: [{ type: "text" as const, text }],
          };
        } catch (error) {
          return {
            content: [
              {
                type: "text" as const,
                text: `Error fetching content: ${error instanceof Error ? error.message : "Unknown error"}`,
              },
            ],
            isError: true,
          };
        }
      }
    );
  • TypeScript interface defining KhanContent - the return type for get_content. Contains id, slug, title, kind, url, description, thumbnailUrl, youtubeId, duration, articleContent, exerciseLength, authorNames, dateAdded, and kaUrl fields.
    export interface KhanContent {
      id: string;
      slug: string;
      title: string;
      kind: ContentKind;
      url: string;
      description: string;
      thumbnailUrl?: string;
      // Video-specific
      youtubeId?: string;
      duration?: number;
      // Article-specific
      articleContent?: string;
      // Exercise-specific
      exerciseLength?: number;
      // Common
      authorNames?: string[];
      dateAdded?: string;
      kaUrl: string;
    }
  • The underlying getContent method in KhanClient that fetches content data. Normalizes the slug, checks cache, queries the API via contentForPath(), constructs KhanContent objects, and falls back to scraping if needed.
    async getContent(slugOrUrl: string): Promise<KhanContent | null> {
      const slug = normalizeSlug(slugOrUrl);
      const cacheKey = `content:${slug}`;
      const cached = this.cache.get<KhanContent>(cacheKey);
      if (cached) return cached;
    
      try {
        const result = await this.contentForPath(slug);
    
        if (result?.content) {
          const raw = result.content;
          const content: KhanContent = {
            id: raw.id ?? slug,
            slug: raw.slug ?? raw.nodeSlug ?? slug,
            title: raw.translatedTitle ?? slug,
            kind: (raw.contentKind as ContentKind) ?? detectContentKind(raw.relativeUrl ?? slug),
            url: buildKAUrl(raw.relativeUrl ?? raw.kaUrl ?? slug),
            description: raw.translatedDescription ?? raw.description ?? "",
            thumbnailUrl: raw.imageUrl,
            youtubeId: raw.youtubeId,
            duration: raw.duration,
            authorNames: raw.authorNames,
            dateAdded: raw.dateAdded,
            kaUrl: raw.kaUrl ?? buildKAUrl(raw.relativeUrl ?? slug),
          };
          this.cache.set(cacheKey, content, CACHE_TTL);
          return content;
        }
    
        // If it's a course, return basic info
        if (result?.course) {
          const c = result.course;
          const content: KhanContent = {
            id: c.id ?? slug,
            slug: c.slug ?? slug,
            title: c.translatedTitle ?? slug,
            kind: "Unknown",
            url: buildKAUrl(c.relativeUrl ?? slug),
            description: c.translatedDescription ?? "",
            thumbnailUrl: c.iconPath,
            kaUrl: buildKAUrl(c.relativeUrl ?? slug),
          };
          this.cache.set(cacheKey, content, CACHE_TTL);
          return content;
        }
      } catch {
        // Fall through
      }
    
      // Fallback: scrape
      return await this.scrapeContentPage(slug);
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aicoder2009/khanacademyMCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server