# How the Module Search and Usage System Works in revit-mcp

> Discover how the revit-mcp module search and usage system dynamically finds and executes Revit operations at runtime. Enable AI clients to introspect and invoke tools seamlessly without hardcoded commands.

- Repository: [MCP servers for Revit/revit-mcp](https://github.com/mcp-servers-for-revit/revit-mcp)
- Tags: internals
- Published: 2026-02-16

---

**The module search and usage system in revit-mcp dynamically discovers available tools at runtime through `search_modules` and executes them via `use_module`, enabling AI clients to introspect and invoke Revit operations without hardcoded commands.**

The `revit-mcp` repository implements a Model Context Protocol (MCP) server that exposes Revit functionality as callable tools for AI assistants. At the heart of this architecture lies the **module search and usage system**, which provides runtime introspection capabilities that allow clients to discover available operations and execute them dynamically.

## Dynamic Tool Discovery Architecture

### The Registration Pipeline in src/tools/register.ts

The foundation of the module search and usage system rests in [`src/tools/register.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/tools/register.ts), which exports the `registerTools` function. This function performs a filesystem scan of the `src/tools` directory at runtime, dynamically importing each tool module and invoking its registration function.

```typescript
// src/tools/register.ts
export async function registerTools(server: McpServer) {
  const __filename = fileURLToPath(import.meta.url);
  const __dirname = path.dirname(__filename);

  // All files in the same directory …
  const files = fs.readdirSync(__dirname);

  // … that are *.ts or *.js but **not** index/register itself
  const toolFiles = files.filter(
    (file) =>
      (file.endsWith(".ts") || file.endsWith(".js")) &&
      file !== "index.ts" &&
      file !== "index.js" &&
      file !== "register.ts" &&
      file !== "register.js"
  );

  // For each candidate…
  for (const file of toolFiles) {
    try {
      // Build the import path that points to the compiled .js file
      const importPath = `./${file.replace(/\.(ts|js)$/, ".js")}`;

      // Dynamically import the module (e.g. "./get_selected_elements.js")
      const module = await import(importPath);

      // Find the exported function whose name starts with “register”
      const registerFunctionName = Object.keys(module).find(
        (key) => key.startsWith("register") && typeof module[key] === "function"
      );

      // Call it – the function itself will call `server.tool(...)`
      if (registerFunctionName) {
        module[registerFunctionName](server);
        console.error(`已注册工具: ${file}`);
      } else {
        console.warn(`警告: 在文件 ${file} 中未找到注册函数`);
      }
    } catch (error) {
      console.error(`注册工具 ${file} 时出错:`, error);
    }
  }
}

```

### Tool Registration Pattern

Each tool file follows a consistent pattern, exporting a `registerXTool` function that receives the `McpServer` instance. This function calls `server.tool()` to define the operation's name, description, Zod schema, and handler. For example, [`src/tools/get_selected_elements.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/tools/get_selected_elements.ts) exports `registerGetSelectedElementsTool` which registers the tool with the server.

## The Introspection Tools

### How search_modules Works

The `search_modules` tool is generated at build time in [`src/tools/search_modules.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/tools/search_modules.ts). When invoked, it returns a complete catalogue of all registered tools, including their names, descriptions, and argument schemas. This enables AI clients to understand available Revit operations without prior knowledge.

The tool handler calls an internal API equivalent to `server.getRegisteredTools()` to retrieve the list of all tools that have been registered by `registerTools`, then returns a JSON array containing each tool's metadata.

### How use_module Works

Complementing the discovery mechanism, `use_module` (implemented in [`src/tools/use_module.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/tools/use_module.ts)) acts as a universal executor. It accepts a `module` name and an `args` object, looks up the corresponding registered tool, and forwards the arguments to that tool's handler.

The tool definition uses a Zod schema with `module: z.string()` and `args: z.any()`. When executed, the handler performs an internal lookup equivalent to `server.getToolByName(module)` and invokes the located tool's handler with the supplied arguments.

## Runtime Execution Flow

### Server Initialization in src/index.ts

The entry point at [`src/index.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/index.ts) initializes the `McpServer` and triggers the tool registration process before connecting the transport layer.

```typescript
// src/index.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { registerTools } from "./tools/register.js";

const server = new McpServer({ name: "revit-mcp", version: "1.0.0" });

async function main() {
  // ① Register *all* tools in src/tools
  await registerTools(server);

  // ② Connect the MCP transport (e.g. stdio for Claude Desktop)
  const transport = new StdioServerTransport();
  await server.connect(transport);
}

```

When `main()` runs, `registerTools` performs the filesystem scan and dynamic imports, registering every tool including `search_modules` and `use_module`.

### Client Interaction Pattern

When an AI client connects to the revit-mcp server, it typically follows a two-step workflow:

1. **Discovery**: Call `search_modules` to retrieve the complete tool catalogue with schemas
2. **Execution**: Call `use_module` with the selected tool name and validated arguments

This pattern eliminates the need for clients to hardcode available operations, allowing the server to add new Revit tools simply by adding new files to the `src/tools` directory.

## Practical Usage Examples

### Discovering Available Modules

A client requests the tool catalogue:

```json
{
  "command": "search_modules",
  "arguments": {}
}

```

**Typical response** (truncated):

```json
{
  "content": [
    {
      "type": "text",
      "text": "[\n  {\n    \"name\": \"get_selected_elements\",\n    \"description\": \"Get elements currently selected in Revit…\",\n    \"schema\": {\"type\":\"object\",\"properties\":{\"limit\":{\"type\":\"number\"}},\"required\":[]}\n  },\n  {\n    \"name\": \"create_line_based_element\",\n    \"description\": \"Create line based element (wall, beam, pipe)\",\n    \"schema\": {…}\n  }\n]"
    }
  ]
}

```

### Invoking a Discovered Module

To call `get_selected_elements` with a limit of 10:

```json
{
  "command": "use_module",
  "arguments": {
    "module": "get_selected_elements",
    "args": { "limit": 10 }
  }
}

```

**Result**:

```json
{
  "content": [
    {
      "type": "text",
      "text": "{\n  \"elements\": [\n    {\"id\": 123, \"type\": \"Wall\", \"name\": \"Wall A\"}\n  ]\n}"
    }
  ]
}

```

Internally, `use_module` fetched the registered handler for `get_selected_elements` (registered by `registerGetSelectedElementsTool` in [`src/tools/get_selected_elements.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/tools/get_selected_elements.ts)) and executed it with the supplied arguments.

## Summary

- The `registerTools` function in [`src/tools/register.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/tools/register.ts) dynamically discovers tool modules by scanning the filesystem and filtering for `.ts` and `.js` files
- Each tool exports a `registerXTool` function that calls `server.tool()` to register with the MCP server, defining the tool's schema and handler
- `search_modules` provides runtime introspection of all registered tools, returning names, descriptions, and Zod schemas
- `use_module` enables dynamic invocation of any registered tool by name, acting as a universal executor that forwards arguments to the appropriate handler
- This architecture allows AI assistants to discover and invoke Revit operations without prior knowledge of available commands

## Frequently Asked Questions

### What is the purpose of the module search and usage system in revit-mcp?

The system enables AI clients to dynamically discover available Revit operations through `search_modules` and execute them via `use_module`. This eliminates the need for clients to maintain hardcoded lists of available tools, allowing the server to add new functionality simply by adding files to the `src/tools` directory.

### How does registerTools discover new tool modules?

The `registerTools` function scans the `src/tools` directory at runtime, filtering for files ending in `.ts` or `.js` while excluding `index` and `register` files. It dynamically imports each module using `import()`, then searches for exported functions starting with "register" and invokes them to register the tool with the MCP server.

### Can I add custom tools to revit-mcp?

Yes. Create a new file in `src/tools/` that exports a `registerCustomTool(server)` function calling `server.tool()` with your tool's name, Zod schema, and handler. The dynamic registration system in [`src/tools/register.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/tools/register.ts) will automatically discover and register your tool on the next server restart, making it available through both `search_modules` and `use_module`.

### What happens if use_module is called with an invalid module name?

If `use_module` receives a module name that doesn't exist in the registry, the handler returns an error indicating that the requested tool was not found. This prevents execution of undefined operations and provides clear feedback to the client about available tool names.