<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Qamar Aftab</title>
    <description>The latest articles on DEV Community by Qamar Aftab (@qamar_aftab_533855f09824f).</description>
    <link>https://dev.to/qamar_aftab_533855f09824f</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4226979%2F1722a75d-74b2-4e51-9ce6-2f2669b0f2cb.png</url>
      <title>DEV Community: Qamar Aftab</title>
      <link>https://dev.to/qamar_aftab_533855f09824f</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/qamar_aftab_533855f09824f"/>
    <language>en</language>
    <item>
      <title>I gave AI agents a video downloader: building a remote MCP server in Python</title>
      <dc:creator>Qamar Aftab</dc:creator>
      <pubDate>Wed, 04 Nov 2026 18:35:18 +0000</pubDate>
      <link>https://dev.to/qamar_aftab_533855f09824f/i-gave-ai-agents-a-video-downloader-building-a-remote-mcp-server-in-python-5eb5</link>
      <guid>https://dev.to/qamar_aftab_533855f09824f/i-gave-ai-agents-a-video-downloader-building-a-remote-mcp-server-in-python-5eb5</guid>
      <description>&lt;p&gt;AI assistants can write code and summarise articles, but when I asked one to "download this video" it had no way to do it. I had already built &lt;a href="https://savedock.pro" rel="noopener noreferrer"&gt;SaveDock&lt;/a&gt;, a free video and audio downloader on Django, yt-dlp and Celery, so I gave agents a way to use it: an MCP server.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is MCP?
&lt;/h2&gt;

&lt;p&gt;The Model Context Protocol (MCP) is an open standard that lets an AI client, such as Claude or Cursor, call tools that live on a server. The client lists the tools, reads their descriptions, and decides when to use them. You don't write a plugin for each assistant. You write one server.&lt;/p&gt;

&lt;h2&gt;
  
  
  The design: a thin adapter, not a second app
&lt;/h2&gt;

&lt;p&gt;My first idea was to put yt-dlp inside the MCP server. I dropped it. The download logic, rate limits and storage already existed behind a REST API, so the MCP server only talks to that API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;agent --MCP--&amp;gt; mcp_server --REST--&amp;gt; /api/v1 --&amp;gt; Celery worker --&amp;gt; storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MCP process has no Django, no yt-dlp and no database. Rate limits, quotas and error handling stay in one place, so the website, the API and MCP cannot drift apart.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four tools
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_video_info(url)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Title, duration and available qualities (read-only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;download_video(url, quality, audio_only)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Starts a download and waits briefly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_download_status(job_id)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Checks a running job&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cancel_download(job_id)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stops a job&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Downloads can take longer than an agent wants to wait, so &lt;code&gt;download_video&lt;/code&gt; waits up to 25 seconds and then returns a job id. The agent can poll with &lt;code&gt;get_download_status&lt;/code&gt;. The result is a temporary link: the file itself is never put into the conversation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Errors an agent can act on
&lt;/h2&gt;

&lt;p&gt;A failure returns &lt;code&gt;isError: true&lt;/code&gt; with a stable code and a &lt;code&gt;retryable&lt;/code&gt; flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"retryable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That way the model can decide whether to retry, wait, or tell the user what went wrong instead of guessing from a stack trace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Free access without opening the door
&lt;/h2&gt;

&lt;p&gt;There is no signup, but there is a limit: 10 downloads per hour per IP. The catch is that all MCP callers arrive from the MCP server's own address. So the server forwards each caller's IP to the API (only when a trusted proxy sits in front), and the limit applies per user, not per server. A caller who passes a wrong API key gets a 401, not a silent downgrade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http video-downloader https://mcp.savedock.pro/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then ask your assistant: "What qualities are available for this video?" or "Save the audio of this video as MP3."&lt;/p&gt;

&lt;p&gt;Docs and a hosted endpoint: &lt;a href="https://savedock.pro/mcp-server/" rel="noopener noreferrer"&gt;https://savedock.pro/mcp-server/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Keep the MCP layer thin. Put business logic behind one API.&lt;/li&gt;
&lt;li&gt;Return structured errors, not just text.&lt;/li&gt;
&lt;li&gt;Design tools for waiting: agents time out sooner than downloads finish.&lt;/li&gt;
&lt;li&gt;Anonymous access needs a per-user limit, or one heavy user blocks everyone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only use it on content you have the right to download. Feedback is welcome in the comments, especially from people building MCP servers.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>python</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
