<?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: Amir Pournasserian</title>
    <description>The latest articles on DEV Community by Amir Pournasserian (@pournasserian).</description>
    <link>https://dev.to/pournasserian</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%2F1487471%2F7a03a69c-2bd6-40f8-b316-4a54e469af06.png</url>
      <title>DEV Community: Amir Pournasserian</title>
      <link>https://dev.to/pournasserian</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pournasserian"/>
    <language>en</language>
    <item>
      <title>I made my website answer ChatGPT and Claude, in .NET</title>
      <dc:creator>Amir Pournasserian</dc:creator>
      <pubDate>Sat, 17 Oct 2026 12:59:52 +0000</pubDate>
      <link>https://dev.to/pournasserian/i-made-my-website-answer-chatgpt-and-claude-in-net-3n79</link>
      <guid>https://dev.to/pournasserian/i-made-my-website-answer-chatgpt-and-claude-in-net-3n79</guid>
      <description>&lt;p&gt;People now ask an assistant before they open a search box. When someone asks ChatGPT or Claude about something I wrote, the answer comes from whatever the model remembers, or from a web search that may or may not land on my pages. I wanted the assistant to be able to ask my site directly and quote the page it used.&lt;/p&gt;

&lt;p&gt;The Model Context Protocol (MCP) is the standard way to do that. A site exposes a server with a few tools, and any MCP client can call them. So I built mcp-my-site: a command-line indexer that crawls a website, and a small ASP.NET Core app that serves the result as a remote MCP server. It is MIT licensed and aimed at small sites, under 1,000 pages.&lt;/p&gt;

&lt;p&gt;You can try it before reading further. A live instance indexes my own site:&lt;br&gt;
&lt;/p&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 pournasserian https://ai.pournasserian.com/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is also a &lt;a href="https://ai.pournasserian.com/playground" rel="noopener noreferrer"&gt;playground&lt;/a&gt;, which is an MCP client in the browser, so you can call the tools without installing anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 60-second version
&lt;/h2&gt;

&lt;p&gt;You need the .NET 10 SDK and, for this first run, nothing else. No API key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--global&lt;/span&gt; mcpmysite
mcpmysite init https://example.com &lt;span class="nt"&gt;--keyword-only&lt;/span&gt;   &lt;span class="c"&gt;# writes appsettings.json&lt;/span&gt;
mcpmysite build                                     &lt;span class="c"&gt;# crawls the site into ./index&lt;/span&gt;
mcpmysite search &lt;span class="s2"&gt;"how can I contact you"&lt;/span&gt;            &lt;span class="c"&gt;# checks the index&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 5000:8080 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;/index:/index:ro"&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;Index__Path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/index ghcr.io/pournasserian/mcp-my-site:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server is then at &lt;code&gt;http://localhost:5000&lt;/code&gt;. &lt;code&gt;/mcp&lt;/code&gt; is the endpoint for agents, &lt;code&gt;/playground&lt;/code&gt; lets you try it in a browser, &lt;code&gt;/api/search?q=contact&lt;/code&gt; returns JSON, and &lt;code&gt;/healthz&lt;/code&gt; shows the loaded index. Connect Claude Code with:&lt;br&gt;
&lt;/p&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 mysite http://localhost:5000/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without Docker you can run the server from a clone of the repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design choices
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Hybrid search, with a fallback.&lt;/strong&gt; Keyword search matches words. Vector search matches meaning, so a question phrased differently from the page still finds it. The server runs BM25 and vector search over embeddings and fuses the two rankings per query. If the embedding provider is unavailable at query time, it falls back to keyword search and says so in the response. The live demo runs hybrid.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It works without a key.&lt;/strong&gt; I did not want the first run to start with signing up for something. &lt;code&gt;--keyword-only&lt;/code&gt; builds an index with no vectors, contacts no embeddings endpoint and reads no key. You give up matching by meaning: "how do I reach you" will not find a page that only says "contact". When you want that, change one setting, set &lt;code&gt;AI_API_KEY&lt;/code&gt;, and rebuild. The embeddings endpoint can be OpenRouter, which is the default, or any OpenAI-compatible one: OpenAI, Azure OpenAI, Gemini, Ollama, a LiteLLM proxy. The key is read from the environment and never written to a file. With the default model, a small site embeds for a fraction of a cent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shaped for the ChatGPT connector contract.&lt;/strong&gt; The server speaks MCP over Streamable HTTP and exposes two tools, &lt;code&gt;search&lt;/code&gt; and &lt;code&gt;fetch&lt;/code&gt;. They follow the shape ChatGPT connectors expect: every search result has an &lt;code&gt;id&lt;/code&gt;, and &lt;code&gt;fetch&lt;/code&gt; takes that &lt;code&gt;id&lt;/code&gt; and returns the page as text. Other clients can pass a &lt;code&gt;url&lt;/code&gt; to &lt;code&gt;fetch&lt;/code&gt; instead. The server also sends instructions generated from the site itself, so the model knows what the site is about and when to use the tools. It works with Claude Code, Claude Desktop, VS Code, Cursor and the MCP Inspector.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One site per server.&lt;/strong&gt; There is no multi-tenancy and no admin UI. Configuration is a file, &lt;code&gt;appsettings.json&lt;/code&gt;. The indexer extracts the main content of each page automatically, so there are no CSS selectors to maintain. The server polls the index source and swaps in a new build without a restart, so updating means building in CI and publishing the index folder to a URL the server polls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A search API for your own site.&lt;/strong&gt; The same search code backs &lt;code&gt;GET /api/search&lt;/code&gt;, with CORS, rate limiting and an OpenAPI document. The user guide has a drop-in snippet for a search box.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does not do
&lt;/h2&gt;

&lt;p&gt;It indexes plain HTML pages reachable over HTTP, or a folder of built HTML. JavaScript-rendered sites, PDFs and pages behind a login are out of scope. A build is always a full rebuild, though unchanged chunks are not re-embedded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Repository: &lt;a href="https://github.com/pournasserian/mcp-my-site" rel="noopener noreferrer"&gt;https://github.com/pournasserian/mcp-my-site&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Live demo: &lt;a href="https://ai.pournasserian.com" rel="noopener noreferrer"&gt;https://ai.pournasserian.com&lt;/a&gt; (playground at &lt;code&gt;/playground&lt;/code&gt;, API reference at &lt;code&gt;/docs&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;NuGet: &lt;a href="https://www.nuget.org/packages/mcpmysite" rel="noopener noreferrer"&gt;https://www.nuget.org/packages/mcpmysite&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docker image: &lt;code&gt;ghcr.io/pournasserian/mcp-my-site&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Feedback I would like
&lt;/h2&gt;

&lt;p&gt;Three things would help most. First, whether the quick start worked for you as written, and where it did not. Second, how it behaves on your site: pages that were extracted badly, or questions that returned the wrong page. The indexer writes a &lt;code&gt;report.json&lt;/code&gt; that lists low-text pages, and attaching that to an issue helps. Third, which MCP clients you tried it with, especially ChatGPT connectors and anything I have not listed.&lt;/p&gt;

&lt;p&gt;Questions and ideas go to &lt;a href="https://github.com/pournasserian/mcp-my-site/discussions" rel="noopener noreferrer"&gt;Discussions&lt;/a&gt;, bugs to &lt;a href="https://github.com/pournasserian/mcp-my-site/issues" rel="noopener noreferrer"&gt;Issues&lt;/a&gt;.&lt;/p&gt;

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