XIV MCP

Example: a job across plugins

A complete plugin whose job uses its own tool, XIV MCP's tools and another plugin's tool.

This page builds Restock Helper, a small plugin that keeps a list of consumables topped up. It does only the part that is its own, deciding what is short, and leaves everything else to tools that already exist:

StepToolFromUses if installed
1. Decide what to buyrestock_planRestock HelperNothing
2. Buy itbuy_itemXIV MCPItem Vendor Location, vnavmesh, Lifestream
3. Walk back to a bellnavigate_toXIV MCPvnavmesh, Lifestream
4. Tell the free companychatbridge_postChat Bridge, another pluginChat Bridge 2.1.0 or newer

Chat Bridge is made up for this example. Swap in any plugin that offers its tools through XIV MCP.

The plugin

Start from XivMcpClient.cs, copied into the plugin.

Plugin.cs
using System.Linq;
using Dalamud.Game.Command;
using Dalamud.IoC;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using XivMcp.Client;

namespace RestockHelper;

public sealed class Plugin : IDalamudPlugin
{
    [PluginService]
    private static IChatGui Chat { get; set; } = null!;

    [PluginService]
    private static ICommandManager Commands { get; set; } = null!;

    private readonly XivMcpClient mcp;

    public Plugin(IDalamudPluginInterface pluginInterface)
    {
        mcp = new XivMcpClient(pluginInterface);

        // 1. Our own part: what is short. It only reads, so it needs
        //    no capabilities and runs without asking.
        mcp.AddTool(
            new McpToolDefinition(
                "restock_plan",
                "Which consumable on the player's restock list is " +
                "shortest, and how many to buy to fill it up.")
            {
                ReadOnly = true,
            },
            args => Shortest());

        // 2. The tools our job uses that are not ours.
        mcp.UsesTools(
            McpDependency.BuiltIn("buy_item"),
            McpDependency.BuiltIn("navigate_to"),
            McpDependency.FromPlugin(
                tool: "chatbridge_post",
                plugin: "ChatBridge",
                pluginName: "Chat Bridge",
                repo: "https://example.com/chatbridge/repo.json",
                minVersion: "2.1.0"));

        Commands.AddHandler("/restock", new CommandInfo(OnRestock)
        {
            HelpMessage = "Buy what is short, then go back to a bell.",
        });
    }

    private void OnRestock(string command, string arguments)
    {
        if (!mcp.IsAvailable)
        {
            Chat.PrintError("XIV MCP is not loaded.", "Restock Helper");
            return;
        }

        // 3. Check first, so the player learns what is missing here
        //    instead of from a refused job.
        var missing = mcp.GetStatus().Dependencies
            .Where(d => !d.Ok).ToList();
        if (missing.Count > 0)
        {
            foreach (var d in missing)
                Chat.PrintError($"{d.Tool}: {d.Message}", "Restock Helper");
            Chat.Print("See /xivmcp → Third-party plugins.",
                "Restock Helper");
            return;
        }

        // 4. One job across three plugins. Results flow between steps.
        try
        {
            var job = mcp.StartJob("Restock",
                new McpJobStep("restock_plan", Id: "plan"),
                new McpJobStep("buy_item", new
                {
                    item = "{{plan.item}}",
                    quantity = "{{plan.quantity}}",
                }),
                new McpJobStep("navigate_to",
                    new { destination = "summoning_bell" }),
                new McpJobStep("chatbridge_post", new
                {
                    channel = "fc",
                    text = "Restocked {{plan.quantity}} {{plan.item}}.",
                }));
            Chat.Print($"Started job {job["id"]}.", "Restock Helper");
        }
        catch (McpToolException ex)
        {
            Chat.PrintError(ex.Message, "Restock Helper");
        }
    }

    private static object Shortest() =>
        new { item = "Grade 8 Tincture of Strength", quantity = 20 };

    public void Dispose()
    {
        Commands.RemoveHandler("/restock");
        mcp.Dispose();
    }
}

What happens

At startup the plugin registers restock_plan and declares its three other tools. The player gets the usual notification and enables Restock Helper in /xivmcp → Third-party plugins.

On its card, under Tools its jobs use, the player sees all three. Say vnavmesh is missing and Chat Bridge is not installed. Then buy_item and navigate_to show a grey hint: they still work, but XIV MCP will put the flag on the map and wait for the player to walk. chatbridge_post shows a red error with Chat Bridge's repository and version, and the tab label says that something is missing.

/restock checks GetStatus().Dependencies first and prints what is missing in chat, instead of starting a job that would be refused.

Once everything is there, /restock starts the job. Each step is checked against its own tool's settings:

  • restock_plan reads, under Restock Helper's Reading.
  • buy_item follows Market & purchases; anything not paid in gil also asks in the game.
  • navigate_to follows Game & navigation.
  • chatbridge_post follows Chat Bridge's own capabilities, such as Send chat.

Results flow: {{plan.item}} and {{plan.quantity}} carry the plan into buy_item and into Chat Bridge's message. "{{plan.quantity}}" on its own keeps its type, so buy_item gets the number 20.

Trying the same thing with Hello MCP

Hello MCP declares get_game_status and navigate_to. /hellomcp bell starts a job that walks to a summoning bell with navigate_to, then prints the result with its own hellomcp_print. Disable vnavmesh in the plugin installer, and Hello MCP's card shows a hint for navigate_to, with a button to enable it again. The job still runs: XIV MCP flags the bell and waits while you walk.

On this page