Docs Connect MCP Servers Create a Tool Create an MCP Tool Page options Copy as Markdown Copied! View as plain text Ask AI about this topic Add MCP server to VS Code After deploying your first MCP server, create custom tools that AI clients can discover and invoke. This guide walks you through the process using any Redpanda Connect component. After reading this page, you will be able to: Create a tool file with the correct structure and MCP metadata Map MCP parameters to component configuration fields using Bloblang Test tools locally before connecting AI clients Prerequisites At least version 4.66.1 of Redpanda Connect installed You can describe the MCP execution model (see The MCP execution model) You have chosen the right component type for your use case (see Choose the right component type) Set up the project If you don’t have an MCP server project yet, create one: rpk connect mcp-server init <project-name> cd <project-name> This command creates the following structure: <project-name>/ ├── resources/ ├── caches/ # Cache components ├── inputs/ # Input components ├── outputs/ # Output components └── processors/ # Processor components (most common) The resources/ directory holds your MCP tool files, organized by component type. Create the tool file Create a YAML file in the appropriate directory. For example, to create a processor tool: touch resources/processors/<tool-name>.yaml The file name can be anything, but use descriptive names that reflect the tool’s purpose. Each YAML file must contain exactly one component. The directory in your project determines the component type: Directory Component type resources/processors/ Processor resources/inputs/ Input resources/outputs/ Output resources/caches/ Cache For example, a file in resources/processors/ defines a processor component. Do not mix component types in the same file. Add the tool structure An MCP tool wraps a Redpanda Connect component and exposes it to AI clients. Each tool file has three parts: Label: The tool name AI clients see Component configuration: A Redpanda Connect component (processor, input, output, or cache) MCP metadata: The tool’s purpose and input parameters Here’s an example using the sql_select processor: label: lookup-customer (1) sql_select: (2) driver: postgres dsn: "${DATABASE_URL}" table: customers columns: ["id", "name", "email", "plan"] where: id = ? args_mapping: '[this.customer_id]' meta: (3) mcp: enabled: true description: "Look up a customer by ID and return their profile." properties: - name: customer_id type: string description: "The customer's unique identifier" required: true 1 The label becomes the tool name that AI clients see and invoke. 2 The sql_select processor queries a PostgreSQL database. 3 The MCP metadata tells AI clients what this tool does and what parameters it accepts. The following sections show how to structure tools for each component type. Label naming rules The label field (tool name) must follow these rules: Lowercase letters, numbers, underscores, and hyphens only (a-z, 0-9, _, -) Cannot start with an underscore No spaces or special characters Valid examples: get-weather, lookup_customer, send-notification-v2 Component types Processors transform, filter, or enrich data. Use a processors: array with one or more processors: label: enrich-order processors: - http: url: "https://api.example.com/lookup" verb: GET meta: mcp: enabled: true description: "Enrich order with customer data" Inputs read data from sources, outputs write data to destinations, and caches store and retrieve data. Define these components directly at the top level. Do not wrap components in input:, output:, or cache: blocks. This syntax is for pipelines, not MCP tools. Input tool label: read-events redpanda: (1) seed_brokers: ["${REDPANDA_BROKERS}"] topics: ["events"] consumer_group: "mcp-reader" meta: mcp: enabled: true description: "Read events from Redpanda" 1 The component name (redpanda) is at the top level, not wrapped in input:. Output tool label: publish-event redpanda: seed_brokers: ["${REDPANDA_BROKERS}"] topic: "processed-events" meta: mcp: enabled: true description: "Publish event to Redpanda" Cache tool label: session-cache memory: default_ttl: 300s meta: mcp: enabled: true description: "In-memory cache for session data" Outputs can include a processors: section to transform data before publishing: Output with processors label: publish-with-timestamp processors: - mutation: | root = this root.published_at = now() redpanda: seed_brokers: ["${REDPANDA_BROKERS}"] topic: "processed-events" meta: mcp: enabled: true description: "Add timestamp and publish to Redpanda" For more examples, see outputs with processors. MCP metadata fields The meta.mcp block defines how AI clients discover and interact with your tool. These fields control tool visibility, naming, and input parameters. Field Required Description enabled Yes Set to true to expose this component as an MCP tool. Set to false to disable without deleting the configuration. description Yes Explains what the tool does and what it returns. AI clients use this to decide when to call the tool. properties No Array of input parameters the tool accepts. See Property fields for the fields in each property. tags No Array of strings for categorizing tools. Use with --tag flag to filter which tools are exposed. Property fields Each entry in the properties array defines an input parameter: Field Required Description name Yes Parameter name. type Yes Data type. Must be one of: string, number, or boolean. description Yes Explains what the parameter is for. Include example values and any constraints. required Yes Set to true if the tool cannot function without this parameter. Property restrictions by component type Different component types have different property capabilities when exposed as MCP tools: Component Type Property Support Details input Only supports the count property AI clients can specify how many messages to read, but you cannot define custom properties. cache No custom properties Properties are hardcoded to key and value for cache operations. output Custom properties supported AI sees properties as an array for batch operations: [{prop1, prop2}, {prop1, prop2}]. processor Custom properties supported You can define any properties needed for data processing operations. Map parameters to component fields When an AI client calls your tool, the arguments object becomes the message body. You can access these arguments using Bloblang, but the syntax depends on where you’re using it: Inside Bloblang contexts (mutation, mapping, args_mapping): Use this.field_name Inside string fields (URLs, topics, headers): Use interpolation ${! json("field_name") } In Bloblang contexts Use this to access message fields directly in processors like mutation, mapping, or in args_mapping fields: mutation: | root.search_query = this.query.lowercase() root.max_results = this.limit.or(10) sql_select: table: orders where: customer_id = ? AND status = ? args_mapping: '[this.customer_id, this.status.or("active")]' In string fields (interpolation) Use ${! … } interpolation to embed Bloblang expressions inside string values like URLs or topic names: http: url: 'https://api.weather.com/v1/current?city=${! json("city") }&units=${! json("units").or("metric") }' redpanda: seed_brokers: ["${REDPANDA_BROKERS}"] (1) topic: '${! json("topic_name") }' (2) 1 ${VAR} without ! is environment variable substitution, not Bloblang. 2 ${! … } with ! is Bloblang interpolation that accesses message data. For more on Bloblang syntax, see Bloblang. For interpolation details, see Interpolation. Provide defaults for optional parameters Use .or(default) to handle missing optional parameters: mutation: | root.city = this.city # Required - will error if missing root.units = this.units.or("metric") # Optional with default root.limit = this.limit.or(10).number() # Optional, converted to number Declare which parameters are required in your meta.mcp.properties: properties: - name: city type: string description: "City name to look up" required: true - name: units type: string description: "Temperature units: 'metric' or 'imperial' (default: metric)" required: false Use secrets and environment variables Never hardcode credentials, API keys, or connection strings in your tool files. Use environment variable substitution to inject secrets at runtime. Reference environment variables using ${VARIABLE_NAME} syntax. Redpanda Connect replaces these placeholders when loading the configuration: http: url: "https://api.example.com/data" headers: Authorization: "Bearer ${API_TOKEN}" sql_select: driver: postgres dsn: "${DATABASE_URL}" table: customers ${VAR} is environment variable substitution (resolved at startup). $\{! expr } is Bloblang interpolation (resolved at runtime from message data). See Map parameters to component fields for the difference. Set environment variables before starting the MCP server: export API_TOKEN="your-secret-token" export DATABASE_URL="postgres://user:password@localhost:5432/mydb" rpk connect mcp-server --address localhost:4195 For production deployments, load secrets from a secrets manager or inject them from your deployment system rather than exporting them in a shell. For naming conventions and security guidelines, see MCP Tool Design. Test the tool Navigate to the root of your MCP project directory. Lint your configuration: rpk connect mcp-server lint Start the MCP server: rpk connect mcp-server --address localhost:8080 Test tool calls using curl: #!/bin/bash # Start SSE connection and capture session ID exec 3< <(curl -s -N http://localhost:8080/sse) read -r line <&3 # event: endpoint read -r line <&3 # data: /sse?sessionid=XXX SESSION_ID=$(echo "$line" | sed 's/.*sessionid=//') echo "Session ID: $SESSION_ID" # Initialize the session curl -s -X POST "http://localhost:8080/message?sessionid=$SESSION_ID" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' sleep 1 # Call your tool (replace with your tool name and arguments) curl -s -X POST "http://localhost:8080/message?sessionid=$SESSION_ID" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"lookup-customer","arguments":{"customer_id":"cust_12345"}}}' # Read SSE responses sleep 2 while read -r -t 1 line <&3; do echo "$line" done exec 3<&- Connect to an AI client and verify the tool appears. For example, with Claude Code: claude mcp add local -- npx mcp-remote http://localhost:8080/sse claude /mcp Test end-to-end with realistic prompts to verify the AI client uses your tool correctly. Control which tools are exposed By default, rpk connect mcp-server exposes all tools in your project that have meta.mcp.enabled: true. Use tags to selectively expose subsets of tools. Add tags to your tool’s metadata: meta: mcp: enabled: true description: "Query production database" tags: - production - database Start the server with the --tag flag to expose only tools matching specific tags. The flag supports regular expressions: # Expose only tools tagged "production" rpk connect mcp-server --tag production # Expose tools with tags starting with "db-" rpk connect mcp-server --tag "db-.*" For guidance on organizing tools by environment, feature, or access level, see Tag strategies. Complete example Here’s a complete tool that wraps the http processor to fetch weather data: label: get-weather processors: # Validate and sanitize input - label: validate_city mutation: | root.city = if this.city.or("").trim() == "" { throw("city is required") } else { this.city.trim().lowercase().re_replace_all("[^a-z\\s\\-]", "") } root.units = this.units.or("metric") # Fetch weather data - label: fetch_weather try: - http: url: 'https://wttr.in/${! json("city") }?format=j1' verb: GET timeout: 10s - mutation: | root.weather = { "location": this.nearest_area.0.areaName.0.value, "country": this.nearest_area.0.country.0.value, "temperature_c": this.current_condition.0.temp_C, "temperature_f": this.current_condition.0.temp_F, "condition": this.current_condition.0.weatherDesc.0.value, "humidity": this.current_condition.0.humidity, "wind_kph": this.current_condition.0.windspeedKmph } # Handle errors gracefully - label: handle_errors catch: - mutation: | root.error = true root.message = "Failed to fetch weather: " + error() meta: mcp: enabled: true description: "Get current weather for a city. Returns temperature, conditions, humidity, and wind speed." properties: - name: city type: string description: "City name (e.g., 'London', 'New York', 'Tokyo')" required: true - name: units type: string description: "Temperature units: 'metric' or 'imperial' (default: metric)" required: false Next steps MCP Tool Design: Apply naming and design guidelines. MCP Tool Patterns: Find patterns for databases, APIs, and Redpanda. Troubleshoot MCP Servers: Diagnose common issues. Components Catalog: Browse all available components. Back to top × Simple online edits For simple changes, such as fixing a typo, you can edit the content directly on GitHub. Edit on GitHub Or, open an issue to let us know about something that you want us to change. Open an issue Contribution guide For extensive content updates, or if you prefer to work locally, read our contribution guide . Was this helpful? thumb_up thumb_down group Ask in the community mail Share your feedback group_add Make a contribution 🎉 Thanks for your feedback! Concepts Best Practices