Enable MCP features for any Gin API with a line of code. Gin-MCP is an opinionated, zero-configuration library that automatically exposes your existing Gin endpoints as Model Context Protocol (MCP) tools, making them instantly usable by MCP-compatible clients like Cursor , Claude Desktop , Continue , Zed , and other MCP-enabled tools. Our philosophy is simple: minimal setup, maximum productivity .
Add this skill
npx mdskills install ckanthony/gin-mcpComprehensive Go library bridge enabling zero-config MCP tool exposure for Gin APIs with excellent docs
1# Gin-MCP: Zero-Config Gin to MCP Bridge23[](https://pkg.go.dev/github.com/ckanthony/gin-mcp)4[](https://github.com/ckanthony/gin-mcp/actions/workflows/ci.yml)5[](https://codecov.io/gh/ckanthony/gin-mcp)678[](https://archestra.ai/mcp-catalog/ckanthony__gin-mcp)910<table border="0">11 <tr>12 <td valign="top">13 <strong>Enable MCP features for any Gin API with a line of code.</strong>14 <br><br>15 Gin-MCP is an <strong>opinionated, zero-configuration</strong> library that automatically exposes your existing Gin endpoints as <a href="https://modelcontextprotocol.io/introduction">Model Context Protocol (MCP)</a> tools, making them instantly usable by MCP-compatible clients like <a href="https://cursor.sh/">Cursor</a>, <a href="https://claude.ai/desktop">Claude Desktop</a>, <a href="https://continue.dev/">Continue</a>, <a href="https://zed.dev/">Zed</a>, and other MCP-enabled tools.16 <br><br>17 Our philosophy is simple: <strong>minimal setup, maximum productivity</strong>. Just plug Gin-MCP into your Gin application, and it handles the rest.18 </td>19 <td valign="top" align="right" width="200">20 <img src="gin-mcp.png" alt="Gin-MCP Logo" width="200"/>21 </td>22 </tr>23</table>2425## Why Gin-MCP?2627- **Effortless Integration:** Connect your Gin API to MCP clients without writing tedious boilerplate code.28- **Zero Configuration (by default):** Get started instantly. Gin-MCP automatically discovers routes and infers schemas.29- **Developer Productivity:** Spend less time configuring tools and more time building features.30- **Flexibility:** While zero-config is the default, customize schemas and endpoint exposure when needed.31- **Existing API:** Works with your existing Gin API - no need to change any code.3233## Demo34353637## Features3839- **Automatic Discovery:** Intelligently finds all registered Gin routes.40- **Schema Inference:** Automatically generates MCP tool schemas from route parameters and request/response types (where possible).41- **Direct Gin Integration:** Mounts the MCP server directly onto your existing `gin.Engine`.42- **Parameter Preservation:** Accurately reflects your Gin route parameters (path, query) in the generated MCP tools.43- **Dynamic BaseURL Resolution:** Support for proxy environments (Quicknode, RAGFlow) with per-user/deployment endpoints.44- **Customizable Schemas:** Manually register schemas for specific routes using `RegisterSchema` for fine-grained control.45- **Selective Exposure:** Filter which endpoints are exposed using operation IDs or tags.46- **Flexible Deployment:** Mount the MCP server within the same Gin app or deploy it separately.4748## Installation4950```bash51go get github.com/ckanthony/gin-mcp52```5354## Basic Usage: Instant MCP Server5556Get your MCP server running in minutes with minimal code:5758```go59package main6061import (62 "net/http"6364 server "github.com/ckanthony/gin-mcp/"65 "github.com/gin-gonic/gin"66)6768func main() {69 // 1. Create your Gin engine70 r := gin.Default()7172 // 2. Define your API routes (Gin-MCP will discover these)73 r.GET("/ping", func(c *gin.Context) {74 c.JSON(http.StatusOK, gin.H{"message": "pong"})75 })7677 r.GET("/users/:id", func(c *gin.Context) {78 // Example handler...79 userID := c.Param("id")80 c.JSON(http.StatusOK, gin.H{"user_id": userID, "status": "fetched"})81 })8283 // 3. Create and configure the MCP server84 // Provide essential details for the MCP client.85 mcp := server.New(r, &server.Config{86 Name: "My Simple API",87 Description: "An example API automatically exposed via MCP.",88 // BaseURL is crucial! It tells MCP clients where to send requests.89 BaseURL: "http://localhost:8080",90 })9192 // 4. Mount the MCP server endpoint93 mcp.Mount("/mcp") // MCP clients will connect here9495 // 5. Run your Gin server96 r.Run(":8080") // Gin server runs as usual97}9899```100101That's it! Your MCP tools are now available at `http://localhost:8080/mcp`. Gin-MCP automatically created tools for `/ping` and `/users/:id`.102103> **Note on `BaseURL`**: Always provide an explicit `BaseURL`. This tells the MCP server the correct address to forward API requests to when a tool is executed by the client. Without it, automatic detection might fail, especially in environments with proxies or different internal/external URLs.104105## Advanced Usage106107While Gin-MCP strives for zero configuration, you can customize its behavior.108109### Annotating Handlers with Comments110111Gin-MCP automatically extracts metadata from handler function comments to generate rich tool descriptions. Use these annotations to make your MCP tools more discoverable and easier to use:112113```go114// listProducts retrieves a paginated list of products115// @summary List all products116// @description Returns a paginated list of products with optional filtering by price, tags, and availability117// @param page Page number for pagination (default: 1)118// @param limit Number of items per page (default: 10, max: 100)119// @param minPrice Minimum price filter120// @param tag Filter products by tag121// @tags public catalog122func listProducts(c *gin.Context) {123 // Handler implementation...124}125```126127**Supported Annotations:**128129- **`@summary`** - Brief one-line description that becomes the tool's primary description130- **`@description`** - Additional detailed explanation appended to the summary131- **`@param <name> <text>`** - Attaches descriptive text to specific input parameters in the generated schema132- **`@tags`** - Space or comma-separated tags used for filtering tools (see "Filtering Exposed Endpoints" below)133- **`@operationId <id>`** - Custom operation ID for the tool (overrides the default `METHOD_path` naming scheme). Must be unique across all routes; duplicates will be skipped (first declaration wins) with a warning logged.134135All annotations are optional, but using them makes your API tools much more user-friendly in MCP clients like Claude Desktop and Cursor.136137**Custom Operation IDs:**138139By default, Gin-MCP generates operation IDs using the format `METHOD_path` (e.g., `GET_users_id`). For routes with very long paths, you can use `@operationId` to specify a shorter, more manageable name:140141```go142// getUserProfile retrieves a user's profile with extended metadata143// @summary Get user profile144// @operationId getUserProfile145// @param id User identifier146func getUserProfile(c *gin.Context) {147 // Instead of the default "GET_api_v2_users_userId_profile_extended"148 // this tool will be named "getUserProfile"149}150```151152**Important:** Operation IDs must be unique. If two handlers use the same `@operationId`, the duplicate will be skipped entirely (first declaration wins), and a warning will always be logged. This ensures consistency between the tool list and operations map.153154### Fine-Grained Schema Control with `RegisterSchema`155156Sometimes, automatic schema inference isn't enough. `RegisterSchema` allows you to explicitly define schemas for query parameters or request bodies for specific routes. This is useful when:157158- You use complex structs for query parameters (`ShouldBindQuery`).159- You want to define distinct schemas for request bodies (e.g., for POST/PUT).160- Automatic inference doesn't capture specific constraints (enums, descriptions, etc.) that you want exposed in the MCP tool definition.161162```go163package main164165import (166 // ... other imports167 "github.com/ckanthony/gin-mcp/pkg/server"168 "github.com/gin-gonic/gin"169)170171// Example struct for query parameters172type ListProductsParams struct {173 Page int `form:"page,default=1" json:"page,omitempty" jsonschema:"description=Page number,minimum=1"`174 Limit int `form:"limit,default=10" json:"limit,omitempty" jsonschema:"description=Items per page,maximum=100"`175 Tag string `form:"tag" json:"tag,omitempty" jsonschema:"description=Filter by tag"`176}177178// Example struct for POST request body179type CreateProductRequest struct {180 Name string `json:"name" jsonschema:"required,description=Product name"`181 Price float64 `json:"price" jsonschema:"required,minimum=0,description=Product price"`182}183184func main() {185 r := gin.Default()186187 // --- Define Routes ---188 r.GET("/products", func(c *gin.Context) { /* ... handler ... */ })189 r.POST("/products", func(c *gin.Context) { /* ... handler ... */ })190 r.PUT("/products/:id", func(c *gin.Context) { /* ... handler ... */ })191192193 // --- Configure MCP Server ---194 mcp := server.New(r, &server.Config{195 Name: "Product API",196 Description: "API for managing products.",197 BaseURL: "http://localhost:8080",198 })199200 // --- Register Schemas ---201 // Register ListProductsParams as the query schema for GET /products202 mcp.RegisterSchema("GET", "/products", ListProductsParams{}, nil)203204 // Register CreateProductRequest as the request body schema for POST /products205 mcp.RegisterSchema("POST", "/products", nil, CreateProductRequest{})206207 // You can register schemas for other methods/routes as needed208 // e.g., mcp.RegisterSchema("PUT", "/products/:id", nil, UpdateProductRequest{})209210 mcp.Mount("/mcp")211 r.Run(":8080")212}213```214215**Explanation:**216217- `mcp.RegisterSchema(method, path, querySchema, bodySchema)`218- `method`: HTTP method (e.g., "GET", "POST").219- `path`: Gin route path (e.g., "/products", "/products/:id").220- `querySchema`: An instance of the struct used for query parameters (or `nil` if none). Gin-MCP uses reflection and `jsonschema` tags to generate the schema.221- `bodySchema`: An instance of the struct used for the request body (or `nil` if none).222223### Filtering Exposed Endpoints224225Control which Gin endpoints become MCP tools using operation IDs or tags. Tags come from the `@tags` annotation in your handler comments (see "Annotating Handlers" above).226227#### Tag-Based Filtering228229Tags are specified in handler function comments using the `@tags` annotation. You can specify tags separated by spaces, commas, or both:230231```go232// listUsers handles user listing233// @summary List all users234// @tags public users235func listUsers(c *gin.Context) {236 // Implementation...237}238239// deleteUser handles user deletion240// @summary Delete a user241// @tags admin, internal242func deleteUser(c *gin.Context) {243 // Implementation...244}245```246247#### Filtering Configuration248249```go250// Only include specific operations by their Operation ID251mcp := server.New(r, &server.Config{252 // ... other config ...253 IncludeOperations: []string{"GET_users", "POST_users"},254})255256// Exclude specific operations257mcp := server.New(r, &server.Config{258 // ... other config ...259 ExcludeOperations: []string{"DELETE_users_id"}, // Don't expose delete tool260})261262// Only include operations tagged with "public" or "users"263// A tool is included if it has ANY of the specified tags264mcp := server.New(r, &server.Config{265 // ... other config ...266 IncludeTags: []string{"public", "users"},267})268269// Exclude operations tagged with "admin" or "internal"270// A tool is excluded if it has ANY of the specified tags271mcp := server.New(r, &server.Config{272 // ... other config ...273 ExcludeTags: []string{"admin", "internal"},274})275```276277**Filtering Rules:**278279- You can only use **one** inclusion filter (`IncludeOperations` **OR** `IncludeTags`).280 - If both are set, `IncludeOperations` takes precedence and a warning is logged.281- You can only use **one** exclusion filter (`ExcludeOperations` **OR** `ExcludeTags`).282 - If both are set, `ExcludeOperations` takes precedence and a warning is logged.283- You **can** combine an inclusion filter with an exclusion filter (e.g., include tag "public" but exclude operation "legacyPublicOp").284- **Exclusion always wins**: If a tool matches both inclusion and exclusion filters, it will be excluded.285- **Tag matching**: A tool is included/excluded if it has **any** of the specified tags (OR logic).286287**Examples:**288289```go290// Include all "public" endpoints but exclude those also tagged "internal"291mcp := server.New(r, &server.Config{292 IncludeTags: []string{"public"},293 ExcludeTags: []string{"internal"},294})295296// Include specific operations but exclude admin endpoints297mcp := server.New(r, &server.Config{298 IncludeOperations: []string{"GET_users", "GET_products"},299 ExcludeTags: []string{"admin"}, // This will be ignored (precedence rule)300})301```302303### Customizing Schema Descriptions (Less Common)304305For advanced control over how response schemas are described in the generated tools (often not needed):306307```go308mcp := server.New(r, &server.Config{309 // ... other config ...310 DescribeAllResponses: true, // Include *all* possible response schemas (e.g., 200, 404) in tool descriptions311 DescribeFullResponseSchema: true, // Include the full JSON schema object instead of just a reference312})313```314315## Examples316317See the [`examples`](examples) directory for complete, runnable examples demonstrating various features:318319### Basic Usage Examples320321- **[`examples/simple/main.go`](examples/simple/main.go)** - Complete product store API with static BaseURL configuration322- **[`examples/simple/quicknode.go`](examples/simple/quicknode.go)** - Dynamic BaseURL configuration for Quicknode proxy environments323- **[`examples/simple/ragflow.go`](examples/simple/ragflow.go)** - Dynamic BaseURL configuration for RAGFlow deployment scenarios324325### Dynamic BaseURL for Proxy Scenarios326327For environments where each user/deployment has a different endpoint (like Quicknode or RAGFlow), you can configure dynamic BaseURL resolution:328329```go330// Quicknode example - resolves user-specific endpoints331mcp := server.New(r, &server.Config{332 Name: "Your API",333 Description: "API with dynamic Quicknode endpoints",334 // No static BaseURL needed!335})336337resolver := server.NewQuicknodeResolver("http://localhost:8080")338mcp.SetExecuteToolFunc(func(operationID string, parameters map[string]interface{}) (interface{}, error) {339 return mcp.ExecuteToolWithResolver(operationID, parameters, resolver)340})341```342343**Environment Variables Supported:**344- **Quicknode**: `QUICKNODE_USER_ENDPOINT`, `USER_ENDPOINT`, `HOST`345- **RAGFlow**: `RAGFLOW_ENDPOINT`, `RAGFLOW_WORKFLOW_URL`, `RAGFLOW_BASE_URL` + `WORKFLOW_ID`346347This eliminates the need for static BaseURL configuration at startup, perfect for multi-tenant proxy environments!348349## Connecting MCP Clients350351Once your Gin application with Gin-MCP is running:3523531. Start your application.3542. In your MCP client, provide the URL where you mounted the MCP server (e.g., `http://localhost:8080/mcp`) as the SSE endpoint:355 - **Cursor**: Settings → MCP → Add Server356 - **Claude Desktop**: Add to MCP configuration file357 - **Continue**: Configure in VS Code settings358 - **Zed**: Add to MCP settings3593. The client will connect and automatically discover the available API tools.360361## Contributing362363Contributions are welcome! Please feel free to submit issues or Pull Requests.364
Full transparency — inspect the skill content before installing.