<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>til &amp;mdash; Nat Knight</title>
    <link>http://natknight.xyz/tag:til</link>
    <description>Reflections, diversions, and opinions from a progressive ex-physicist programmer dad with a sore back.</description>
    <pubDate>Sun, 24 May 2026 14:19:40 -0700</pubDate>
    <item>
      <title>TIL: Copying to the clipboard from a webpage is easy now</title>
      <link>http://natknight.xyz/til-copying-to-the-clipboard-from-a-webpage-is-easy-now</link>
      <description>&lt;![CDATA[#til #webdev #javascript #clipboard&#xA;&#xA;I remember a time when there were whole libraries dedicated to clipboard management.&#xA;&#xA;Turns out in browsers released after ~2020 you can do it with one line:&#xA;&#xA;navigator.clipboard.writeText(text).then(console.log).catch(console.error);&#xA;&#xA;!--more--&#xA;&#xA;Some caveats:&#xA;&#xA;this only works in a secure context (which mostly means either &#34;a site served over HTTPS&#34; or localhost).&#xA;the user needs to have interacted with the page before you can actually call the method.&#xA;this was only added to certain browsers in 2020, so it&#39;s new enough that it might cause problems if your constituents have older devices or don&#39;t update often.&#xA;&#xA;Usually when I do this I&#39;m implementing something like a &#34;copy to clipboard&#34; button:&#xA;&#xA;span id=&#39;copy-target&#39;This text is going into a clipboard/span&#xA;button onclick=&#39;copyToClipboard(&#34;copy-target&#34;)&#39;Copy/button&#xA;script&#xA;    const copyToClipboard = (target) =  {&#xA;        const element = document.getElementById(target);&#xA;        const value = element?.innerText&#xA;        if (value) {&#xA;            navigator.clipboard.writeText(value).then(console.log).catch(console.error);&#xA;        }&#xA;    }&#xA;/script&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p><a href="http://natknight.xyz/tag:til" class="hashtag"><span>#</span><span class="p-category">til</span></a> <a href="http://natknight.xyz/tag:webdev" class="hashtag"><span>#</span><span class="p-category">webdev</span></a> <a href="http://natknight.xyz/tag:javascript" class="hashtag"><span>#</span><span class="p-category">javascript</span></a> <a href="http://natknight.xyz/tag:clipboard" class="hashtag"><span>#</span><span class="p-category">clipboard</span></a></p>

<p>I remember a time when there were <a href="https://github.github.com/clipboard-copy-element/">whole</a> <a href="https://clipboardjs.com/">libraries</a> dedicated to clipboard management.</p>

<p>Turns out in <a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/clipboard#browser_compatibility">browsers released after ~2020</a> you can do it with one line:</p>

<pre><code class="language-javascript">navigator.clipboard.writeText(text).then(console.log).catch(console.error);
</code></pre>



<p>Some caveats:</p>
<ul><li>this only works in a <a href="https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts">secure context</a> (which mostly means either “a site served over HTTPS” or <code>localhost</code>).</li>
<li>the user needs to have interacted with the page before you can actually call the method.</li>
<li>this was only added to certain browsers in 2020, so it&#39;s new enough that it might cause problems if your constituents have older devices or don&#39;t update often.</li></ul>

<p>Usually when I do this I&#39;m implementing something like a “copy to clipboard” button:</p>

<pre><code class="language-html">&lt;span id=&#39;copy-target&#39;&gt;This text is going into a clipboard&lt;/span&gt;
&lt;button onclick=&#39;copyToClipboard(&#34;copy-target&#34;)&#39;&gt;Copy&lt;/button&gt;
&lt;script&gt;
    const copyToClipboard = (target) =&gt; {
        const element = document.getElementById(target);
        const value = element?.innerText
        if (value) {
            navigator.clipboard.writeText(value).then(console.log).catch(console.error);
        }
    }
&lt;/script&gt;
</code></pre>
]]></content:encoded>
      <guid>http://natknight.xyz/til-copying-to-the-clipboard-from-a-webpage-is-easy-now</guid>
      <pubDate>Mon, 10 Mar 2025 21:19:49 +0000</pubDate>
    </item>
    <item>
      <title>TIL: Accessing Content in Custom HTML Elements</title>
      <link>http://natknight.xyz/til-accessing-content-in-custom-html-elements</link>
      <description>&lt;![CDATA[#til #javascript #webdev #customelements&#xA;&#xA;I was working on a custom element today that replaces a textarea with CodeMirror in the UI while still updating the textarea in the background so that it can be submitted in a form. I ran across a wild footgun in custom elements.&#xA;&#xA;sStackOverflow eventually provided the solution./s There&#39;s a solution on stack overflow, but Danny Engleman wrote up a more thorough explanation.&#xA;&#xA;!--more--&#xA;&#xA;When you&#39;re creating a custom element you do most of your setup in a method called connectedCallback. Here you can read the content of the custom element, replace it with different elements, set up callback handlers, etc.&#xA;&#xA;Or so I thought.&#xA;&#xA;You can read the attributes of the node just fine, but if you try to read its contents you&#39;ll find they aren&#39;t there. The reason is that the connectedCallback fires when the opening tag of the custom element is parsed, so the contents aren&#39;t in the DOM yet.&#xA;&#xA;The solution is breathtakingly simple and silly: call setTimeout to defer initialization until after the DOM has been fully parsed.&#xA;&#xA;export class MyCustomElement extends HTMLElement {&#xA;  initialize () {&#xA;    // Fancy custom element stuff goes here; you can safely access&#xA;    // this.innerHTML and stuff.&#xA;  }&#xA;&#xA;  connectedCallback() {&#xA;    // Be sure to use an arrow function here or &#39;this&#39; will be&#xA;    // messed up in `initialize.&#xA;    setTimeout(() =  this.initialize(), 0)&#xA;  }&#xA;}&#xA;`]]&gt;</description>
      <content:encoded><![CDATA[<p><a href="http://natknight.xyz/tag:til" class="hashtag"><span>#</span><span class="p-category">til</span></a> <a href="http://natknight.xyz/tag:javascript" class="hashtag"><span>#</span><span class="p-category">javascript</span></a> <a href="http://natknight.xyz/tag:webdev" class="hashtag"><span>#</span><span class="p-category">webdev</span></a> <a href="http://natknight.xyz/tag:customelements" class="hashtag"><span>#</span><span class="p-category">customelements</span></a></p>

<p>I was working on a custom element today that replaces a <code>textarea</code> with <a href="https://codemirror.net/">CodeMirror</a> in the UI while still updating the <code>textarea</code> in the background so that it can be submitted in a form. I ran across a <em>wild</em> footgun in custom elements.</p>

<p><s>StackOverflow eventually provided <a href="https://stackoverflow.com/questions/64169068/obtain-this-textcontent-during-custom-element-construction">the solution</a>.</s> There&#39;s a solution on stack overflow, but Danny Engleman wrote up a <a href="https://dev.to/dannyengelman/web-component-developers-do-not-connect-with-the-connectedcallback-yet-4jo7">more thorough explanation</a>.</p>



<p>When you&#39;re creating a custom element you do most of your setup in a method called <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#using_a_custom_element"><code>connectedCallback</code></a>. Here you can read the content of the custom element, replace it with different elements, set up callback handlers, etc.</p>

<p><em>Or so I thought.</em></p>

<p>You can read the <em>attributes</em> of the node just fine, but if you try to read its <em>contents</em> you&#39;ll find they aren&#39;t there. The reason is that the <code>connectedCallback</code> fires when the <em>opening</em> tag of the custom element is parsed, so the contents aren&#39;t in the DOM yet.</p>

<p>The solution is breathtakingly simple and silly: call <code>setTimeout</code> to defer initialization until after the DOM has been fully parsed.</p>

<pre><code class="language-javascript">export class MyCustomElement extends HTMLElement {
  initialize () {
    // Fancy custom element stuff goes here; you can safely access
    // `this.innerHTML` and stuff.
  }

  connectedCallback() {
    // Be sure to use an arrow function here or &#39;this&#39; will be
    // messed up in `initialize.
    setTimeout(() =&gt; this.initialize(), 0)
  }
}
</code></pre>
]]></content:encoded>
      <guid>http://natknight.xyz/til-accessing-content-in-custom-html-elements</guid>
      <pubDate>Sun, 09 Mar 2025 21:59:00 +0000</pubDate>
    </item>
    <item>
      <title>TIL: uv --script</title>
      <link>http://natknight.xyz/til-uv-script</link>
      <description>&lt;![CDATA[#uv #python #til #scripting&#xA;&#xA;I&#39;ve known for a while that uv can run Python scripts that declare inline dependencies.&#xA;&#xA;I learned today that you can also use uv to manage those dependencies.&#xA;&#xA;!--more--&#xA;&#xA;If you have a file foo.py:&#xA;&#xA;!/usr/bin/env -S uv run&#xA;&#xA;print(&#34;your code here&#34;)&#xA;&#xA;you can run uv add --script foo.py numpy and it becomes:&#xA;&#xA;!/usr/bin/env -S uv run&#xA;/// script&#xA;requires-python = &#34;  =3.12&#34;&#xA;dependencies = [&#xA;&#34;numpy&#34;,&#xA;]&#xA;///&#xA;&#xA;print(&#34;your code here&#34;)&#xA;&#xA;This is obviously nice if, like me, you have trouble remembering the exact syntax for declaring the inline dependencies, but it also means uv does things like dependency resolution so you don&#39;t have to manage your script&#39;s dependencies by hand.&#xA;&#xA;You can add --script to:&#xA;&#xA;uv add and uv remove to add and remove dependencies&#xA;uv lock and uv sync to create and synchronize with the script&#39;s lock file&#xA;uv tree to print the script&#39;s transitive dependencies&#xA;uv export to export the script&#39;s dependencies to another format, such as requirements.txt&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p><a href="http://natknight.xyz/tag:uv" class="hashtag"><span>#</span><span class="p-category">uv</span></a> <a href="http://natknight.xyz/tag:python" class="hashtag"><span>#</span><span class="p-category">python</span></a> <a href="http://natknight.xyz/tag:til" class="hashtag"><span>#</span><span class="p-category">til</span></a> <a href="http://natknight.xyz/tag:scripting" class="hashtag"><span>#</span><span class="p-category">scripting</span></a></p>

<p>I&#39;ve known for a while that <code>uv</code> can <a href="https://docs.astral.sh/uv/guides/scripts/#declaring-script-dependencies">run Python scripts</a> that declare <a href="https://packaging.python.org/en/latest/specifications/inline-script-metadata/#inline-script-metadata">inline dependencies</a>.</p>

<p>I learned today that you can also use <code>uv</code> to <em>manage</em> those dependencies.</p>



<p>If you have a file <code>foo.py</code>:</p>

<pre><code class="language-python">#!/usr/bin/env -S uv run

print(&#34;your code here&#34;)
</code></pre>

<p>you can run <code>uv add --script foo.py numpy</code> and it becomes:</p>

<pre><code class="language-python">#!/usr/bin/env -S uv run
# /// script
# requires-python = &#34;&gt;=3.12&#34;
# dependencies = [
#     &#34;numpy&#34;,
# ]
# ///

print(&#34;your code here&#34;)
</code></pre>

<p>This is obviously nice if, like me, you have trouble remembering the exact syntax for declaring the inline dependencies, but it also means <code>uv</code> does things like dependency resolution so you don&#39;t have to manage your script&#39;s dependencies by hand.</p>

<p>You can add <code>--script</code> to:</p>
<ul><li><code>uv add</code> and <code>uv remove</code> to add and remove dependencies</li>
<li><code>uv lock</code> and <code>uv sync</code> to create and synchronize with the script&#39;s lock file</li>
<li><code>uv tree</code> to print the script&#39;s transitive dependencies</li>
<li><code>uv export</code> to export the script&#39;s dependencies to another format, such as <code>requirements.txt</code></li></ul>
]]></content:encoded>
      <guid>http://natknight.xyz/til-uv-script</guid>
      <pubDate>Tue, 04 Mar 2025 17:59:28 +0000</pubDate>
    </item>
    <item>
      <title>TIL: Time-stamp based dependency constraints with uv</title>
      <link>http://natknight.xyz/til-time-stamp-based-dependency-constraints-with-uv</link>
      <description>&lt;![CDATA[#til #python #uv&#xA;&#xA;(via epistasis on HN)&#xA;&#xA;Semantic versioning is difficult. Not everyone has the same idea of what &#34;breaking change&#34; means, and depending on your language and tooling &#34;breaking&#34; changes can sneak in despite your best efforts.&#xA;&#xA;One possible mitigation is to record when you resolved your dependencies and ignore anything published after that date. It&#39;s crude, and relies on package dependencies not monkeying with stuff that&#39;s already been published, but it&#39;s easy to understand and you can do it with uv.&#xA;&#xA;It&#39;s documented here.&#xA;&#xA;!---more---&#xA;&#xA;Put a date in your pyproject.toml file and subsequent invocations of uv will ignore anything published after the cutoff:&#xA;&#xA;[tool.uv]&#xA;exclude-newer = &#34;2023-10-16T00:00:00Z&#34;&#xA;&#xA;You can also use it in the &#34;inline metadata&#34; format for scripting! From the uv docs:&#xA;&#xA;/// script&#xA;dependencies = [&#xA;&#34;requests&#34;,&#xA;]&#xA;[tool.uv]&#xA;exclude-newer = &#34;2023-10-16T00:00:00Z&#34;&#xA;///&#xA;&#xA;import requests&#xA;&#xA;print(requests.version)&#xA;&#xA;This use case seems particularly valuable: if I write a quick script I probably care more about it not breaking than I care about it getting updated dependencies. It&#39;s nice that uv offers a way to keep code running rather than forcing me to update it or throw it out.]]&gt;</description>
      <content:encoded><![CDATA[<p><a href="http://natknight.xyz/tag:til" class="hashtag"><span>#</span><span class="p-category">til</span></a> <a href="http://natknight.xyz/tag:python" class="hashtag"><span>#</span><span class="p-category">python</span></a> <a href="http://natknight.xyz/tag:uv" class="hashtag"><span>#</span><span class="p-category">uv</span></a></p>

<p>(via <a href="https://news.ycombinator.com/item?id=43097209">epistasis on HN</a>)</p>

<p>Semantic versioning is difficult. Not everyone has the same idea of what “breaking change” means, and depending on your language and tooling “breaking” changes can sneak in despite your best efforts.</p>

<p>One possible mitigation is to record <em>when</em> you resolved your dependencies and ignore anything published after that date. It&#39;s crude, and relies on package dependencies not monkeying with stuff that&#39;s already been published, but it&#39;s easy to understand and you can do it with <a href="https://docs.astral.sh/uv/"><code>uv</code></a>.</p>

<p>It&#39;s documented <a href="https://docs.astral.sh/uv/guides/scripts/#improving-reproducibility">here</a>.</p>



<p>Put a date in your <code>pyproject.toml</code> file and subsequent invocations of <code>uv</code> will ignore anything published after the cutoff:</p>

<pre><code class="language-toml">[tool.uv]
exclude-newer = &#34;2023-10-16T00:00:00Z&#34;
</code></pre>

<p>You can also use it in the <a href="https://docs.astral.sh/uv/guides/scripts/#declaring-script-dependencies">“inline metadata” format for scripting</a>! From the <code>uv</code> docs:</p>

<pre><code class="language-python"># /// script
# dependencies = [
#   &#34;requests&#34;,
# ]
# [tool.uv]
# exclude-newer = &#34;2023-10-16T00:00:00Z&#34;
# ///

import requests

print(requests.__version__)
</code></pre>

<p>This use case seems particularly valuable: if I write a quick script I probably care more about it not breaking than I care about it getting updated dependencies. It&#39;s nice that <code>uv</code> offers a way to keep code running rather than forcing me to update it or throw it out.</p>
]]></content:encoded>
      <guid>http://natknight.xyz/til-time-stamp-based-dependency-constraints-with-uv</guid>
      <pubDate>Thu, 20 Feb 2025 23:56:36 +0000</pubDate>
    </item>
    <item>
      <title>TIL: Local development against &#34;S3&#34;</title>
      <link>http://natknight.xyz/til-local-development-against-s3</link>
      <description>&lt;![CDATA[#til #s3 #dockercompose&#xA;&#xA;I had to update a Django service to use S3 instead of local file storage recently. Here&#39;s how I set up Minio (a self-hostable S3-compatible data store) for local development.&#xA;&#xA;!--more--&#xA;&#xA;I use Docker Compose (well, Finch Compose, but same difference) to run local versions of Postgres and S3.&#xA;&#xA;Here&#39;s the docker-compose.yaml file for Minio:&#xA;&#xA;services:&#xA;  minio:&#xA;    image: minio/minio&#xA;    ports:&#xA;      &#34;64040:9000&#34; # API port&#xA;      &#34;64041:9001&#34; # Web dashboard port&#xA;    volumes:&#xA;      miniodata:/data&#xA;    environment:&#xA;      MINIOROOTUSER: minioadmin&#xA;      MINIOROOTPASSWORD: minioadmin&#xA;    command: server /data --console-address &#34;:9001&#34;&#xA;&#xA;volumes:&#xA;  miniodata:&#xA;&#xA;Note that:&#xA;&#xA;We&#39;re setting a default username and password  for local development. Obviously use proper secrets management for a more serious environment.&#xA;We&#39;re re-mapping the service&#39;s ports to a random high port since other services sometimes try to bind 9000 or 9001.&#xA;&#xA;Once you&#39;ve docker compose up&#39;d this, you can log in to the admin console at localhost:64041 and create your development bucket and access keys.&#xA;&#xA;In your application you&#39;ll need to configure:&#xA;&#xA;An S3 url:&#xA;  use http://127.0.0.1:64040 for running natively or&#xA;  http://minio:9000 if you&#39;re in a different Docker Compose container&#xA;An S3 region: use us-east-1&#xA;An S3 bucket name: use whatever you created via the admin&#xA;An access key and secret key: use the keys you created via the admin&#xA;&#xA;This should also work for developing against S3 compatible services like:&#xA;&#xA;Backblaze B2&#xA;Cloudflare R2&#xA;Digital Ocean Spaces&#xA;Oracle Cloud&#xA;Scaleway Object Storage&#xA;&#xA;etc.&#xA;&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p><a href="http://natknight.xyz/tag:til" class="hashtag"><span>#</span><span class="p-category">til</span></a> <a href="http://natknight.xyz/tag:s3" class="hashtag"><span>#</span><span class="p-category">s3</span></a> <a href="http://natknight.xyz/tag:dockercompose" class="hashtag"><span>#</span><span class="p-category">dockercompose</span></a></p>

<p>I had to update a Django service to use S3 instead of local file storage recently. Here&#39;s how I set up <a href="https://min.io/">Minio</a> (a self-hostable S3-compatible data store) for local development.</p>



<p>I use Docker Compose (well, <a href="https://runfinch.com/docs/getting-started/compose/">Finch Compose</a>, but same difference) to run local versions of Postgres and S3.</p>

<p>Here&#39;s the <code>docker-compose.yaml</code> file for Minio:</p>

<pre><code class="language-yaml">services:
  minio:
    image: minio/minio
    ports:
      - &#34;64040:9000&#34; # API port
      - &#34;64041:9001&#34; # Web dashboard port
    volumes:
      - minio_data:/data
    environment:
      MINIO_ROOT_USER: minioadmin
      MINIO_ROOT_PASSWORD: minioadmin
    command: server /data --console-address &#34;:9001&#34;

volumes:
  minio_data:
</code></pre>

<p>Note that:</p>
<ul><li>We&#39;re setting a default username and password  for local development. <strong>Obviously use proper secrets management for a more serious environment</strong>.</li>
<li>We&#39;re re-mapping the service&#39;s ports to a random high port since other services sometimes try to bind <code>9000</code> or <code>9001</code>.</li></ul>

<p>Once you&#39;ve <code>docker compose up</code>&#39;d this, you can log in to the admin console at <code>localhost:64041</code> and create your development bucket and access keys.</p>

<p>In your application you&#39;ll need to configure:</p>
<ul><li>An S3 url:
<ul><li>use <code>http://127.0.0.1:64040</code> for running natively or</li>
<li><code>http://minio:9000</code> if you&#39;re in a different Docker Compose container</li></ul></li>
<li>An S3 region: <a href="https://github.com/minio/minio/discussions/15063">use <code>us-east-1</code></a></li>
<li>An S3 bucket name: use whatever you created via the admin</li>
<li>An access key and secret key: use the keys you created via the admin</li></ul>

<p>This should also work for developing against S3 compatible services like:</p>
<ul><li><a href="https://www.backblaze.com/docs/cloud-storage-s3-compatible-api">Backblaze B2</a></li>
<li><a href="https://developers.cloudflare.com/r2/api/s3/api/">Cloudflare R2</a></li>
<li><a href="https://www.digitalocean.com/products/spaces">Digital Ocean Spaces</a></li>
<li><a href="https://docs.oracle.com/en-us/iaas/Content/Object/Tasks/s3compatibleapi.htm">Oracle Cloud</a></li>
<li><a href="https://www.scaleway.com/en/docs/object-storage/">Scaleway Object Storage</a></li></ul>

<p>etc.</p>
]]></content:encoded>
      <guid>http://natknight.xyz/til-local-development-against-s3</guid>
      <pubDate>Wed, 19 Feb 2025 19:45:06 +0000</pubDate>
    </item>
    <item>
      <title>TIL: Fetching web pages with Claude Desktop</title>
      <link>http://natknight.xyz/til-extending-claude-desktop-with-model-context-protocol-plugins</link>
      <description>&lt;![CDATA[#til #llms #claude #modelcontextprotocol&#xA;&#xA;I was checking out the Model Context Protocol, a spec from Anthropic that lets you expose external programs to an LLM, and discovered that you can extend the Claude desktop app in this way. There are a bunch of enterprisey tools (and you can write your own, more interesting stuff), but out-of-the box you can enable URL-fetching, which lets you grab and process arbitrary information with Claude.&#xA;&#xA;!--more--&#xA;&#xA;To enable the plugin,&#xA;&#xA;Install the Claude desktop app.&#xA;Enable MCP under &#34;Settings&#34;   &#34;Developer&#34;   &#34;Edit Config&#34;&#xA;Add details about the MCP plugins you want in the indicated file.&#xA;&#xA;For fetch, you&#39;d add:&#xA;&#xA;{&#xA;  &#34;mcpServers&#34;: {&#xA;    &#34;fetch&#34;: {&#xA;      &#34;command&#34;: &#34;uvx&#34;,&#xA;        &#34;args&#34;: [&#xA;          &#34;mcp-server-fetch&#34;&#xA;        ]&#xA;    }&#xA;  }&#xA;}&#xA;&#xA;Anthropic has more docs on using MCP plugins from the desktop app, and a list of built-in plugins that you can set up with minimal effort.&#xA;&#xA;I think there are some exciting possibilities here! I don&#39;t think it should be too hard to give the model access to reference material, searchable history--basically retrieval augmented generation but for small, bespoke uses that wouldn&#39;t warrant the effort of custom-building an RAG system.&#xA;&#xA;Of course, you&#39;re sending all of that off to Anthropic, so it would be particularly_ interesting to find a system that can do this with a self-hosted LLM, where the privacy and power concerns aren&#39;t as severe.&#xA;&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p><a href="http://natknight.xyz/tag:til" class="hashtag"><span>#</span><span class="p-category">til</span></a> <a href="http://natknight.xyz/tag:llms" class="hashtag"><span>#</span><span class="p-category">llms</span></a> <a href="http://natknight.xyz/tag:claude" class="hashtag"><span>#</span><span class="p-category">claude</span></a> <a href="http://natknight.xyz/tag:modelcontextprotocol" class="hashtag"><span>#</span><span class="p-category">modelcontextprotocol</span></a></p>

<p>I was checking out the <a href="https://www.anthropic.com/news/model-context-protocol">Model Context Protocol</a>, a spec from Anthropic that lets you expose external programs to an LLM, and discovered that you can extend the <a href="https://claude.ai/download">Claude desktop app</a> in this way. There are a bunch of enterprisey tools (and you can write your own, more interesting stuff), but out-of-the box you can enable URL-fetching, which lets you grab and process arbitrary information with Claude.</p>



<p>To enable the plugin,</p>
<ol><li>Install the Claude desktop app.</li>
<li>Enable MCP under “Settings” &gt; “Developer” &gt; “Edit Config”</li>
<li>Add details about the MCP plugins you want in the indicated file.</li></ol>

<p>For <a href="https://github.com/modelcontextprotocol/servers/tree/main/src/fetch">fetch</a>, you&#39;d add:</p>

<pre><code class="language-json">{
  &#34;mcpServers&#34;: {
    &#34;fetch&#34;: {
      &#34;command&#34;: &#34;uvx&#34;,
        &#34;args&#34;: [
          &#34;mcp-server-fetch&#34;
        ]
    }
  }
}
</code></pre>

<p>Anthropic has <a href="https://modelcontextprotocol.io/quickstart/user">more docs</a> on using MCP plugins from the desktop app, and a list of <a href="https://github.com/modelcontextprotocol/servers/tree/main?tab=readme-ov-file#-reference-servers">built-in plugins</a> that you can set up with minimal effort.</p>

<p>I think there are some exciting possibilities here! I don&#39;t think it should be too hard to give the model access to reference material, searchable history—basically <a href="https://en.wikipedia.org/wiki/Retrieval-augmented_generation">retrieval augmented generation</a> but for small, bespoke uses that wouldn&#39;t warrant the effort of custom-building an RAG system.</p>

<p>Of course, you&#39;re sending all of that off to Anthropic, so it would be <em>particularly</em> interesting to find a system that can do this with a self-hosted LLM, where the privacy and power concerns aren&#39;t as severe.</p>
]]></content:encoded>
      <guid>http://natknight.xyz/til-extending-claude-desktop-with-model-context-protocol-plugins</guid>
      <pubDate>Fri, 14 Feb 2025 03:56:14 +0000</pubDate>
    </item>
    <item>
      <title>TIL: Making a Django REST framework view do server-side rendering</title>
      <link>http://natknight.xyz/til-making-a-django-rest-framework-view-do-server-side-rendering</link>
      <description>&lt;![CDATA[#til #django #djangorestramework #python&#xA;&#xA;On a recent project I found myself needing one classic form-and-template style page in an otherwise API-driven project. I could, of course, [just do it] with a regular view function, but I had a bunch of authentication and suchlike set up for DRF APIViews.&#xA;&#xA;Turns out it&#39;s actually pretty easy to make an APIView kick it oldschool!&#xA;&#xA;!--more--&#xA;&#xA;from restframework.parsers import FormParser&#xA;from restframework.renderers import TemplateHTMLRenderer&#xA;from restframework.response import Response&#xA;from restframework.views import APIView&#xA;&#xA;class OldSchoolView(APIView):&#xA;    parserclasses = [FormParser]&#xA;    rendererclasses = [TemplateHTMLRenderer]&#xA;    template_name = &#34;path/to/a/template.html&#34;&#xA;&#xA;    def get(self, request):&#xA;        context = {}&#xA;        # do regular view stuff here&#xA;        return Response(context)&#xA;&#xA;    def post(self, request):&#xA;        formdata = request.data.dict()&#xA;        # do regular view stuff here&#xA;        return Response()  # or redirect; whatever&#39;s appropriate&#xA;&#xA;The indicated template gets rendered with the context you pass to the Response.&#xA;&#xA;I think you could write a view that simultaneously supports HTML and JSON with [content negotiation], but it ended up being more trouble that it was worth in my case.&#xA;&#xA;[just do it]: https://spookylukey.github.io/django-views-the-right-way/anything.html]]&gt;</description>
      <content:encoded><![CDATA[<p><a href="http://natknight.xyz/tag:til" class="hashtag"><span>#</span><span class="p-category">til</span></a> <a href="http://natknight.xyz/tag:django" class="hashtag"><span>#</span><span class="p-category">django</span></a> <a href="http://natknight.xyz/tag:djangorestramework" class="hashtag"><span>#</span><span class="p-category">djangorestramework</span></a> <a href="http://natknight.xyz/tag:python" class="hashtag"><span>#</span><span class="p-category">python</span></a></p>

<p>On a recent project I found myself needing one classic form-and-template style page in an otherwise API-driven project. I could, of course, <a href="https://spookylukey.github.io/django-views-the-right-way/anything.html">just do it</a> with a regular view function, but I had a bunch of authentication and suchlike set up for DRF <code>APIView</code>s.</p>

<p>Turns out it&#39;s actually pretty easy to make an <code>APIView</code> kick it oldschool!</p>



<pre><code class="language-python">from rest_framework.parsers import FormParser
from rest_framework.renderers import TemplateHTMLRenderer
from rest_framework.response import Response
from rest_framework.views import APIView


class OldSchoolView(APIView):
    parser_classes = [FormParser]
    renderer_classes = [TemplateHTMLRenderer]
    template_name = &#34;path/to/a/template.html&#34;

    def get(self, request):
        context = {}
        # do regular view stuff here
        return Response(context)

    def post(self, request):
        formdata = request.data.dict()
        # do regular view stuff here
        return Response()  # or redirect; whatever&#39;s appropriate
</code></pre>

<p>The indicated template gets rendered with the <code>context</code> you pass to the <code>Response</code>.</p>

<p>I think you could write a view that simultaneously supports HTML and JSON with <a href="https://www.django-rest-framework.org/api-guide/content-negotiation/">content negotiation</a>, but it ended up being more trouble that it was worth in my case.</p>
]]></content:encoded>
      <guid>http://natknight.xyz/til-making-a-django-rest-framework-view-do-server-side-rendering</guid>
      <pubDate>Thu, 06 Feb 2025 08:06:18 +0000</pubDate>
    </item>
  </channel>
</rss>