How MCP Prompts Are Converted into Callable Tools with Argument Validation in Dify

MCP prompt templates are dynamically converted into callable tools by generating JSON Schema definitions from declared arguments, prefixing tool names with prompt__, and enforcing input validation against that schema before executing the prompt on the MCP server.

The junjiem/dify-plugin-tools-mcp_sse plugin enables Dify agents to interact with MCP servers that expose prompt templates. When configured to treat prompts as tools, the plugin converts MCP prompts into callable tools with automatic argument validation, allowing LLMs to invoke structured prompt templates using standard tool-calling semantics.

Enabling Prompt-to-Tool Conversion in McpClients

The conversion process begins when the McpClients class is instantiated with the prompts_as_tools parameter set to True. This flag signals the client to fetch prompt definitions from connected MCP servers and transform them into tool-compatible structures.

Located in utils/mcp_client.py, the McpClients class manages server connections and tool discovery. When fetch_tools() is invoked, it iterates through configured servers and, if prompts_as_tools is enabled, calls client.list_prompts() to retrieve available prompt templates alongside standard tools.

Fetching and Transforming MCP Prompt Definitions

For each prompt returned by the MCP server, the plugin constructs a synthetic tool definition that conforms to Dify's tool interface. This transformation occurs within the fetch_tools method and involves three critical steps: schema generation, namespacing, and action classification.

Generating JSON Schema from Prompt Arguments

The plugin inspects each prompt's declared arguments to build a JSON Schema that Dify uses for input validation. For every argument in the prompt's arguments list, the plugin creates a property with type: "string" and includes the argument description.

If the MCP server marks an argument as required, the plugin adds the argument name to the schema's required array. This schema is embedded in the tool's inputSchema field, enabling Dify's tool registry to validate incoming arguments before execution.


# utils/mcp_client.py – schema construction logic

properties = {}
required = []
for arg in prompt.get("arguments", []):
    properties[arg["name"]] = {
        "type": "string",
        "description": arg.get("description", ""),
    }
    if arg.get("required", False):
        required.append(arg["name"])

tool = {
    "name": name,
    "description": f"Use the prompt template '{prompt['name']}' from MCP Server.",
    "inputSchema": {
        "type": "object",
        "properties": properties,
        "required": required,
    },
}

Namespacing and ActionType Classification

To prevent naming collisions between prompts and standard tools, the plugin prefixes each prompt-based tool with prompt__. For example, a prompt named summarize_article becomes prompt__summarize_article. If collisions persist across multiple servers, the server name is prepended as well (e.g., serverName__prompt__summarize_article).

Each synthesized tool is stored in the internal _tool_actions dictionary as a ToolAction object with action_type set to ActionType.PROMPT. This classification distinguishes prompt invocations from standard tool calls during execution.

Argument Validation and Tool Execution

Once registered, prompt-based tools follow the same execution pipeline as standard tools, with validation occurring before the MCP server is contacted.

Schema Validation Before RPC Calls

When the LLM invokes a prompt-based tool, Dify's tool registry validates the provided arguments against the JSON Schema generated during the fetch phase. This validation occurs before McpClients.execute_tool is called, ensuring that missing required fields or type mismatches trigger an immediate error without consuming MCP server resources.

If validation passes, execute_tool retrieves the stored ToolAction and dispatches the call based on the action_type.

Executing Prompts via get_prompt()

For prompt actions, the execution path invokes client.get_prompt() with the original prompt name and the validated arguments. The MCP server renders the prompt template using the provided arguments and returns a list of chat-style messages.

The plugin concatenates these messages into a single text block, prefixing each with its role (e.g., assistant:, user:), and returns the result to Dify as a standard tool response.


# utils/mcp_client.py – prompt execution path

elif action_type == ActionType.PROMPT:
    prompt = tool_action.action_feature
    messages = client.get_prompt(prompt["name"], tool_args)
    text = ""
    for message in messages:
        role = message["role"]
        content = message["content"]
        text += f"{role}: {content.get('text', str(content))}\n"
    tool_contents.append({"type": "text", "text": text.strip()})

Complete Implementation Example

The following example demonstrates initializing the MCP client with prompt-to-tool conversion enabled, retrieving the generated tool catalog, and invoking a prompt-based tool with automatic argument validation.

from utils.mcp_client import McpClients

# Initialize client with prompt-to-tool conversion enabled

clients = McpClients(
    servers_config={
        "exampleSrv": {"url": "http://mcp.example.com", "transport": "sse"}
    },
    prompts_as_tools=True,  # Enable conversion

)

# Fetch tools including synthesized prompt tools

tool_catalog = clients.fetch_tools()
print([t["name"] for t in tool_catalog if t["name"].startswith("prompt__")])

# Output: ['prompt__summarize_article', 'prompt__create_summary']

# Execute a prompt-based tool with validated arguments

result = clients.execute_tool(
    "prompt__summarize_article",
    {"url": "https://example.com/article", "length": "short"}
)
print(result[0]["text"])

# Output: "assistant: Here is a short summary of the article …"

If a required argument is omitted, Dify's validation layer raises an error before the MCP server is contacted:

ToolError: Argument validation failed – missing required property "url".

Summary

  • Enable conversion by setting prompts_as_tools=True when instantiating McpClients in utils/mcp_client.py.
  • Automatic schema generation creates JSON Schema definitions from prompt arguments, marking required fields based on the MCP server's declarations.
  • Namespacing prevents collisions by prefixing prompt tools with prompt__ and optionally the server name.
  • Pre-execution validation ensures arguments conform to the schema before RPC calls are made to the MCP server.
  • Execution flow maps tool calls to client.get_prompt(), rendering the template server-side and returning concatenated messages to the LLM.

Frequently Asked Questions

How does the plugin prevent naming conflicts between prompts and standard tools?

The plugin prefixes every prompt-based tool with prompt__ during the conversion process. If a name collision still occurs across multiple MCP servers, it prepends the server name to create a unique identifier (e.g., serverName__prompt__prompt_name). This ensures that prompt tools remain distinct from standard MCP tools and from each other.

What happens if an argument fails validation?

Dify's tool registry validates incoming arguments against the JSON Schema generated when the prompt was converted into a tool. If a required argument is missing or a type mismatch occurs, the validation fails immediately and raises a ToolError before McpClients.execute_tool invokes the MCP server. This prevents malformed requests from consuming server resources or producing invalid prompt renders.

Can prompts with no arguments be converted into tools?

Yes. When a prompt has no declared arguments, the plugin generates an empty properties dictionary and an empty required array in the JSON Schema. The resulting tool accepts an empty argument object {} and executes the prompt template without parameters when invoked, returning the static or server-rendered content to the LLM.

Where is the prompt execution logic located?

The execution logic resides in the execute_tool method of the McpClients class within utils/mcp_client.py. When the action_type is ActionType.PROMPT, the method calls client.get_prompt() with the prompt name and validated arguments, then processes the returned message list into a text response for Dify.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →