How to Create MCP Server Desktop Extension in TypeScript

About two weeks ago, Anthropic announced MCP Server Desktop Extension (DXT), which “makes installing MCP servers as easy as clicking a button.”

Claude Desktop Extensions: One-click MCP server installation for Claude Desktop \ Anthropic

It looks fun, so I developed a small DXT in TypeScript last week. The project is small and the documentation is good enough, and it actually took me only a day or two. However, there are a few points not covered in the documentation, so I decided to write this blog post.

Overview

Target audience

This post requires you to have basic knowledge of MCP and JavaScript/TypeScript.

What to implement

We have a product called WP RAG, which is a solution that consists of a WordPress plugin and a backend API with a vector database. It imports content from the customer’s WordPress site, generates embeddings in the vector database, and offers search/RAG capabilities. See the following pages for more details:

What I developed is an MCP server that works as a bridge between MCP clients (e.g., Claude Desktop) and WP RAG API, and offers an MCP “tool” that searches through WordPress content.

Below is the source code:
mobalab/wp-rag-mcp-server-dxt

Resources to check out

Before proceeding, I would like you to go through the following online resources if you haven’t yet:

Implementation

Now that you have a basic understanding of the background and DXT, let’s start implementing a DXT.

Create the manifest file

First, run npx @anthropic-ai/dxt init to initialize manifest.json. Below is my case. You can change the file later, so if you’re not sure what to enter, put anything for now:

$ npx @anthropic-ai/dxt init
This utility will help you create a manifest.json file for your DXT extension.
Press ^C at any time to quit.

✔ Extension name: wp-rag-mcp-server-dxt
✔ Author name: Mobalab, KK
✔ Display name (optional): WP RAG MCP Server Desktop Extension
✔ Version: 0.0.1
✔ Description: Model Context Protocol (MCP) server as part of the "WP RAG" WordPress plugin ecosystem
✔ Add a detailed long description? no
✔ Author email (optional): info@mobalab.net
✔ Author URL (optional): https://mobalab.net
✔ Homepage URL (optional): https://services.mobalab.net/wp-rag/
✔ Documentation URL (optional):
✔ Support URL (optional):
✔ Icon file path (optional, relative to manifest):
✔ Add screenshots? no
✔ Server type: Node.js
✔ Entry point: server/index.js
✔ Does your MCP Server provide tools you want to advertise (optional)? yes
✔ Tool name: search_posts
✔ Tool description (optional): Semantic search with score thresholds
✔ Add another tool? no
✔ Does your server generate additional tools at runtime? no
✔ Does your MCP Server provide prompts you want to advertise (optional)? no
✔ Add compatibility constraints? no
✔ Add user-configurable options? yes
✔ Configuration option key (unique identifier): api_key
✔ Option type: String
✔ Option title (human-readable name): WP RAG API Key
✔ Option description: WP RAG API Key
✔ Is this option required? yes
✔ Is this option sensitive (like a password)? yes
✔ Add another configuration option? yes
✔ Configuration option key (unique identifier): site_id
✔ Option type: Number
✔ Option title (human-readable name): Site ID on WP RAG
✔ Option description: Site ID on WP RAG
✔ Is this option required? yes
✔ Is this option sensitive (like a password)? no
✔ Add min/max constraints? no
✔ Add another configuration option? no
✔ Keywords (comma-separated, optional): WordPress,RAG,vector database
✔ License: MIT
✔ Add repository information? yes
✔ Repository URL: https://github.com/mobalab/wp-rag-mcp-server-dxt

Created manifest.json at /Users/kashima/Documents/workspace/rag/wp-rag-mcp-server-dxt/manifest.json

Next steps:
1. Ensure all your production dependencies are in this directory
2. Run 'dxt pack' to create your .dxt file

Set up a TypeScript project

We’ll build a DXT in TypeScript, so we need to set up the project for that. I’m not that familiar with the JavaScript/TypeScript ecosystem, so I had Gemini CLI create the necessary files, which are:

  • package.json
  • tsconfig.json
  • .gitignore

I adopted the basic folder structure: the source files go into src, and transpiled JS files go into dist.

Update manifest.json: entry_point

The example in the documentation uses plain JavaScript, but we want to use TypeScript, so the setup is a bit different. In particular, the entry point in the example is server/index.js while ours is dist/index.js.

I initially thought the directory server had a special meaning, but it seems like the entry point can be any file in any directory.

Update manifest.json: user_config

WP RAG is a multi-tenant application, so the user should be able to pass the ID and the credentials to the WP RAG API server (through the MCP server) in order to access their data in WP RAG. The user_config section in manifest.json is for that kind of purpose. After installing a DXT with values in user_config, the user will see the config dialog like the following:

I added two values to user_config. The values entered by the user are passed to the program as environment variables, WP_RAG_SITE_ID and WP_RAG_API_KEY. Please see the source for the details.

Write code

My code is pretty simple and easy to understand. Only the code snippet below needs a bit of explanation:

const SearchPostsInputSchema = z.object({
  q: z.string().describe("Search query"),
  limit: z.number().optional().default(4).describe("Number of results"),
  score_threshold: z.number().optional().default(0.2).describe("Threshold for score"),
});

server.registerTool(
  "search_posts",
  {
    title: "Search WordPress posts from WP RAG vector database",
    description: "Search posts from a specific site using a query and optional filters.",
    inputSchema: SearchPostsInputSchema.shape,
  },
  async ({ q, limit, score_threshold }) => {
    # (snip)
  }
);

Look at the arguments to registerTool. The first one is the name of the tool and the second one describes what this tool does. Also, SearchPostsInputSchema is the list of the arguments that the tool takes. All the information will be used by MCP clients, so it’s important.

Package and test locally

Create a package

Now the development is almost done. Run the following command to create a .dxt file:

npx @anthropic-ai/dxt pack

Install it

If there are no errors, double-click the generated file. Alternatively, drag and drop it into Claude Desktop’s Settings window.

Then, click on the “Install” button, fill in the config values, and click on the “Save” button. Also, don’t forget to enable it by clicking on the toggle button.

Test

Open a chat, and type in a question: “Search for WordPress posts about WP RAG, and summarize what the software can do in English.”

Claude thinks it needs to use the tool provided by WP RAG MCP Server Desktop Extension, and asks you for permission:

After you allow it, Claude calls the tool, receives the response, and generates an answer based on it:

As the documentation recommends, you should test it on both Windows and macOS.

Distribute

Once you’re satisfied with the extension, it’s time to distribute it. Actually, there are two ways:

  • Distribute it by yourself
  • Through Anthropic’s directory

As for the former, the easiest way would be using GitHub’s releases feature. Please see the following document for the details:

Managing releases in a repository – GitHub Docs

The latter requires a review by Anthropic. You can submit the extension on the following page:

Interest Form: Desktop Extensions

Conclusion

MCP Server Desktop Extension gives end-users, who are not necessarily developers, an easy way to install MCP servers. At the same time, it gives developers an easy way to package and distribute MCP servers.

This blog post walks through how to develop, test and distribute an MCP Server Desktop Extension written in TypeScript. Developing an extension in TypeScript isn’t much different from that using plain JavaScript, nor is it different from developing other types of programs in TypeScript.

If you have any questions etc., feel free to ask me via the channels shown below:

we are hiring

優秀な技術者と一緒に、好きな場所で働きませんか

株式会社もばらぶでは、優秀で意欲に溢れる方を常に求めています。働く場所は自由、働く時間も柔軟に選択可能です。

現在、以下の職種を募集中です。ご興味のある方は、リンク先をご参照下さい。

コメントを残す