Create a New MCP Tool

Last modified by Vincent Massol on 2026/09/04 19:23

Steps

Implement a custom MCP tool as an XWiki component. Your module needs the application-ai-llm-mcp-api dependency, which carries the MCPTool role, the MCPToolSupport parameter builder and the access doors, plus mcp-core for the MCP SDK types and xwiki-commons-component-api for the component framework.

  1. Create the component class, implementing org.xwiki.contrib.llm.mcp.MCPTool.
    @Component
    @Named("my_tool")
    @Singleton
    public class MyTool implements MCPTool
    {
        @Override
        public McpSchema.Tool getToolDefinition()
        {
            // Return tool name, description, and input JSON schema
        }
    
        @Override
        public McpSchema.CallToolResult execute(McpSchema.CallToolRequest request)
        {
            // Implement the tool logic
        }
    }
  2. Declare the component in src/main/resources/META-INF/components.txt, which is hand-maintained: a component missing from it does not exist at runtime.
    com.example.MyTool
  3. Declare the parameters once, with MCPToolSupport.builder(). It produces both the advertised MCP input schema and the typed argument accessors, so schema and parsing cannot drift apart.
    private static final MCPToolSupport PARAMS = MCPToolSupport.builder()
        .requiredString("query", "The text to search for.")
        .integer("limit", "Maximum number of results (default 10).")
        .build();

    Build the advertised definition from that same object, so the schema is never written out a second time:

    @Override
    public McpSchema.Tool getToolDefinition()
    {
        return McpSchema.Tool.builder(TOOL_ID, PARAMS.inputSchema())
            .description("Does something useful.")
            .build();
    }

    Where the advertised schema varies with cross-wiki reach, hold both variants in MCPReachAwareParams and advertise the applicable one: parsing always uses the superset, only the advertised schema differs.

  4. Enforce authorization through MCPDocumentAccess before loading any document, rather than resolving the reference yourself.
    @Inject
    private MCPDocumentAccess documentAccess;
    
    // Before any operation:
    documentAccess.resolveAndAuthorize(reference, Right.VIEW);

    Where a tool does not operate on documents, check wiki-level access with the platform's ContextualAuthorizationManager.

  5. Return actionable results, using isError results whose message names the corrective action.
    return MCPToolSupport.errorResult("Error: 'offset' must be a non-negative integer.");
    return MCPToolSupport.result("Found: " + count + " results.");
  6. Set the tool metadata that the man catalog is built from.
    @Override
    public String getCategory()
    {
        return "Search & Navigation";
    }
    
    @Override
    public String getSummary()
    {
        return "A one-line summary for the man catalog.";
    }
    
    @Override
    public String getManPage()
    {
        return """
            EXAMPLES
                Query the wiki for documentation:
                    my_tool query="API reference"
            """;
    }
  7. Install your extension in XWiki, then ask a connected agent to call man: the tool is discovered and registered on the next request, with no restart, and appears in the catalog under its category.
    Search & Navigation
        my_tool           A one-line summary for the man catalog.

FAQ

Can my tool be disabled by the administrator?

Yes. Per-wiki visibility goes through the enabledTools configuration list, which your tool id must match to be registered on that wiki, and isEnabled() is a global kill switch you can override. Override isWrite() to return true where your tool modifies content: write tools are off by default per wiki.

Related

Get Connected