{"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"custom-mcp-tools","__idx":0},"children":["Custom MCP tools"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Add your own tools to the project's ",{"$$mdtype":"Tag","name":"Link","attributes":{"href":"/docs/realm/customization/mcp-server"},"children":["MCP server"]},"."," ","Create an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["@mcp"]}," folder in your project, and add one ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".ts"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".js"]}," file for each tool."," ","The AI model calls your tools from a script that it runs with the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["execute"]}," tool of the MCP server."," ","The file name is the tool name, and the script calls the tool as a camelCase function: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["@mcp/get-weather.ts"]}," becomes ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tools.getWeather"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"create-a-tool","__idx":1},"children":["Create a tool"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A tool file has one default export with a description, JSON Schemas for the input and the result, and an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["execute"]}," function:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"ts","data-title":"@mcp/get-weather.ts","header":{"title":"@mcp/get-weather.ts","controls":{"copy":{}}},"source":"import type { McpToolDefinition } from '@redocly/config';\n\nconst tool: McpToolDefinition<{ city: string }> = {\n  description: 'Get the current temperature in a city.',\n  inputSchema: {\n    type: 'object',\n    required: ['city'],\n    properties: {\n      city: { type: 'string', description: 'City name, for example \"Berlin\".' },\n    },\n  },\n  outputSchema: {\n    type: 'object',\n    required: ['city', 'temperatureC'],\n    properties: {\n      city: { type: 'string' },\n      temperatureC: { type: 'number', description: 'Current temperature, in °C.' },\n    },\n  },\n  annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },\n  async execute({ city }) {\n    const response = await fetch(`https://wttr.in/${encodeURIComponent(city)}?format=j1`);\n    if (!response.ok) {\n      throw new Error(`Weather lookup failed: ${response.status}`);\n    }\n    const data = await response.json();\n    return { city, temperatureC: Number(data.current_condition[0].temp_C) };\n  },\n};\n\nexport default tool;\n","lang":"ts"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["McpToolDefinition"]}," type import is optional."," ","For type checks in a local editor, install ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["@redocly/config"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["@types/node"]}," in your project."," ","The Reunite editor loads these types automatically."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can put tool files in subfolders of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["@mcp"]},", or in more than one ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["@mcp"]}," folder in your project."," ","Only the file name sets the tool name, so ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["@mcp/weather/get-weather.ts"]}," still becomes ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tools.getWeather"]},"."," ","If two tool files anywhere in your project have the same name, the build fails."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"tool-definition","__idx":2},"children":["Tool definition"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The default export supports the following properties:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Property"},"children":["Property"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Type"},"children":["Type"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["description"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["string"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["REQUIRED."]}," ","Description that the AI model reads to decide when to call the tool."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["execute"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["function"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["REQUIRED."]}," ","Tool implementation."," ","Receives ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["(args, context, extra)"]}," and returns a ",{"$$mdtype":"Tag","name":"Link","attributes":{"href":"#tool-result"},"children":["tool result"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["inputSchema"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["object"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["JSON Schema for the tool arguments."," ","The schema must describe an object."," ","Default: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{ type: 'object', properties: {} }"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["outputSchema"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["object"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["JSON Schema for the value that ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["execute"]}," returns."," ","See ",{"$$mdtype":"Tag","name":"Link","attributes":{"href":"#describe-the-result"},"children":["Describe the result"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["annotations"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["object"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Behavior hints, as the MCP specification defines them:"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["readOnlyHint"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["destructiveHint"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["idempotentHint"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["openWorldHint"]},"."," ","The server combines the hints of all tools into the hints of the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["execute"]}," tool."," ","Set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["readOnlyHint"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["destructiveHint"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["openWorldHint"]}," on every tool."," ","A tool without them counts as a destructive write that can reach external systems, and AI clients then ask the user to confirm every ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["execute"]}," call."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["isAvailable"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["function"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Decides whether the current caller can see and call the tool."," ","Receives the same ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["context"]}," object as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["execute"]}," and returns a boolean or a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Promise<boolean>"]},"."," ","See ",{"$$mdtype":"Tag","name":"Link","attributes":{"href":"#availability-and-authorization"},"children":["Availability and authorization"]},"."]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"execute-function","__idx":3},"children":["Execute function"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["execute"]}," function receives three arguments:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Argument"},"children":["Argument"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Type"},"children":["Type"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["args"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["object"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Input arguments from the MCP client, validated against ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["inputSchema"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["context"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"Link","attributes":{"href":"#context"},"children":["McpToolContext"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Information about the project and the current user."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["extra"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["object"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["MCP protocol metadata: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["requestId"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sessionId"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["authInfo"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["requestInfo"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["_meta"]},"."]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"context","__idx":4},"children":["Context"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The most useful ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["context"]}," properties:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Property"},"children":["Property"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Type"},"children":["Type"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["user"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["object"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Current user: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["email"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["teams"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["claims"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["isAuthenticated"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["idpAccessToken"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["config"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["object"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Resolved project configuration from ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["redocly.yaml"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["baseUrl"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["string"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Origin of the current request."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["accessToken"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["string"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Access token of the authenticated MCP connection, when present."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["metadata"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["object"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Free-form caller metadata from the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-Redocly-AI-Metadata"]}," request header."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["fs"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["object"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Read access to the built project output."," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fs.readFile(path)"]}," returns the text of a file, and rejects paths outside the output directory."," ","When the project publishes ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["llms.txt"]},", the output also has a Markdown copy of each page."," ","RBAC does not apply to these reads: the output contains restricted pages too."," ","Before you return file content, check that ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["context.user"]}," can access it."]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"tool-result","__idx":5},"children":["Tool result"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The value that your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["execute"]}," function returns is the result of the tool call in the AI model's script."," ","Return a JSON-serializable value, such as an object, an array, or a string."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To report a failure, throw an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Error"]}," from ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["execute"]},"."," ","The tool call in the script, for example ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tools.getWeather()"]},", then throws an error with the same message."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"describe-the-result","__idx":6},"children":["Describe the result"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Declare an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["outputSchema"]}," for each tool."," ","The AI model reads the TypeScript signature of each tool from the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["describe-tools"]}," tool."," ","The server makes the result type of the signature from ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["outputSchema"]},"."," ","Without an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["outputSchema"]},", the result type is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["unknown"]},", and the model must guess the field names."," ","A wrong guess makes the script fail, or return empty data without an error."," ","The build logs a warning for each tool without an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["outputSchema"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The server does not validate the result against ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["outputSchema"]},", so keep the schema and the returned value the same."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"availability-and-authorization","__idx":7},"children":["Availability and authorization"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The project access controls apply to the entire MCP server."," ","If the project requires login or restricts content with ",{"$$mdtype":"Tag","name":"Link","attributes":{"href":"/docs/realm/config/access/rbac"},"children":["RBAC"]},", the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/mcp"]}," endpoint requires authentication."," ","In these projects, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["context.user"]}," identifies the caller."," ","In other projects, every caller is anonymous."," ","If you enable ",{"$$mdtype":"Tag","name":"Link","attributes":{"href":"/docs/realm/config/mcp"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["mcp.docs.publicEndpoint"]}]},", anonymous clients can also call your tools at ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/mcp-public"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To limit a tool to specific users or teams, add an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["isAvailable"]}," function."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["isAvailable"]}," returns ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["false"]},", the server removes the tool from ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["describe-tools"]}," and from the script."," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["isAvailable"]}," does not get the tool arguments, so do checks that depend on the arguments in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["execute"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The following tool finds API error codes in a data file of the project."," ","Only the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["partners"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["support"]}," teams can use the tool."," ","Only the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["support"]}," team gets the internal error codes:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"ts","data-title":"data/error-codes.ts","header":{"title":"data/error-codes.ts","controls":{"copy":{}}},"source":"export type ErrorCode = {\n  code: string;\n  message: string;\n  fix: string;\n  internal: boolean;\n};\n\nexport const errorCodes: ErrorCode[] = [\n  {\n    code: 'E1042',\n    message: 'The API key has expired.',\n    fix: 'Create a new API key on the Credentials page.',\n    internal: false,\n  },\n  {\n    code: 'E9001',\n    message: 'The billing service did not respond.',\n    fix: 'Retry the request. If it fails again, restart the billing sync job.',\n    internal: true,\n  },\n];\n","lang":"ts"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"ts","data-title":"@mcp/get-error-code.ts","header":{"title":"@mcp/get-error-code.ts","controls":{"copy":{}}},"source":"import type { McpToolDefinition } from '@redocly/config';\nimport { errorCodes } from '../data/error-codes';\n\nconst tool: McpToolDefinition<{ code: string }> = {\n  description: 'Look up an API error code and get the steps to fix the error.',\n  inputSchema: {\n    type: 'object',\n    required: ['code'],\n    properties: {\n      code: {\n        type: 'string',\n        description: 'Error code from an API response, for example \"E1042\".',\n      },\n    },\n  },\n  outputSchema: {\n    type: 'object',\n    required: ['code', 'message', 'fix'],\n    properties: {\n      code: { type: 'string' },\n      message: { type: 'string', description: 'What the error means.' },\n      fix: { type: 'string', description: 'Steps to fix the error.' },\n    },\n  },\n  annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n  isAvailable: (context) =>\n    context.user.teams.includes('partners') || context.user.teams.includes('support'),\n  execute({ code }, context) {\n    const entry = errorCodes.find((item) => item.code === code);\n    if (!entry || (entry.internal && !context.user.teams.includes('support'))) {\n      throw new Error(`Unknown error code \"${code}\".`);\n    }\n    return { code: entry.code, message: entry.message, fix: entry.fix };\n  },\n};\n\nexport default tool;\n","lang":"ts"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The build bundles ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["data/error-codes.ts"]}," into the tool, so the site does not publish the data file."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"constraints","__idx":8},"children":["Constraints"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["A tool name can contain only letters, numbers, hyphens, and underscores, and can have a maximum of 64 characters."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["A tool name must be different from the built-in tool names, for example ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["execute"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["describe-tools"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["search"]},", or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["list-apis"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Each file in an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["@mcp"]}," folder must be a tool."," ","Keep shared helper code outside the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["@mcp"]}," folder."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The build imports each tool file once to read its schemas."," ","Code at the top level of a tool file must not call other services or change data."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If a tool file breaks these rules, the build fails with an error that names the file."," ","In local development, the server reports the error and skips the tool."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"resources","__idx":9},"children":["Resources"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"Link","attributes":{"href":"/docs/realm/customization/mcp-server"},"children":["Model Context Protocol server"]}]}," - Overview of the built-in MCP server and how to connect AI tools to it"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"Link","attributes":{"href":"/docs/realm/config/mcp"},"children":["MCP configuration reference"]}]}," - Enable, disable, and configure the MCP server for your project"]}]}]},"frontmatter":{"products":["Redoc","Revel","Reef","Realm"],"plans":["Enterprise","Enterprise+"]},"tagList":["table"],"title":"Custom MCP tools","lastModified":"2026-10-12T12:06:04.000Z"}