https://github.com/ckanthony/gin-mcp
Enable MCP features for any Gin API with a line of code
https://github.com/ckanthony/gin-mcp
Last synced: 3 months ago
JSON representation
Enable MCP features for any Gin API with a line of code
- Host: GitHub
- URL: https://github.com/ckanthony/gin-mcp
- Owner: ckanthony
- License: mit
- Created: 2025-04-17T17:27:12.000Z (12 months ago)
- Default Branch: main
- Last Pushed: 2025-08-14T11:15:08.000Z (8 months ago)
- Last Synced: 2025-08-14T13:12:26.307Z (8 months ago)
- Language: Go
- Size: 819 KB
- Stars: 46
- Watchers: 1
- Forks: 9
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-mcp - ckanthony/gin-mcp
- awesome-mcp-servers - gin-mcp - A zero-configuration Go library and MCP server that automatically exposes existing Gin web framework APIs as MCP tools, enabling effortless MCP integration for Gin-based services. ([Read more](/details/gin-mcp.md)) `go` `api` `gateway` (Api Integration Mcp Servers)
- awesome-mcp-servers - ckanthony/gin-mcp - یک کتابخانه Go با پیکربندی صفر برای در معرض دید قرار دادن خودکار APIهای موجود چارچوب وب Gin به عنوان ابزارهای MCP. (پیادهسازیهای سرور / 💻 <a name="developer-tools"></a>ابزارهای توسعهدهنده)
- awesome-mcp-servers - **gin-mcp** - Enable MCP features for any Gin API with a line of code `go` `mcp` `api` `web` `http` `go install ckanthony/gin-mcp@latest` (⚙️ DevOps)
- toolsdk-mcp-registry - ❌ gin-mcp
- best-of-mcp-servers - GitHub - 42% open · ⏱️ 07.11.2025) (Developer Tools)
README
# Gin-MCP: Zero-Config Gin to MCP Bridge
[](https://pkg.go.dev/github.com/ckanthony/gin-mcp)
[](https://github.com/ckanthony/gin-mcp/actions/workflows/ci.yml)
[](https://codecov.io/gh/ckanthony/gin-mcp)

[](https://archestra.ai/mcp-catalog/ckanthony__gin-mcp)
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. Just plug Gin-MCP into your Gin application, and it handles the rest.
## Why Gin-MCP?
- **Effortless Integration:** Connect your Gin API to MCP clients without writing tedious boilerplate code.
- **Zero Configuration (by default):** Get started instantly. Gin-MCP automatically discovers routes and infers schemas.
- **Developer Productivity:** Spend less time configuring tools and more time building features.
- **Flexibility:** While zero-config is the default, customize schemas and endpoint exposure when needed.
- **Existing API:** Works with your existing Gin API - no need to change any code.
## Demo

## Features
- **Automatic Discovery:** Intelligently finds all registered Gin routes.
- **Schema Inference:** Automatically generates MCP tool schemas from route parameters and request/response types (where possible).
- **Direct Gin Integration:** Mounts the MCP server directly onto your existing `gin.Engine`.
- **Parameter Preservation:** Accurately reflects your Gin route parameters (path, query) in the generated MCP tools.
- **Dynamic BaseURL Resolution:** Support for proxy environments (Quicknode, RAGFlow) with per-user/deployment endpoints.
- **Customizable Schemas:** Manually register schemas for specific routes using `RegisterSchema` for fine-grained control.
- **Selective Exposure:** Filter which endpoints are exposed using operation IDs or tags.
- **Flexible Deployment:** Mount the MCP server within the same Gin app or deploy it separately.
## Installation
```bash
go get github.com/ckanthony/gin-mcp
```
## Basic Usage: Instant MCP Server
Get your MCP server running in minutes with minimal code:
```go
package main
import (
"net/http"
server "github.com/ckanthony/gin-mcp/"
"github.com/gin-gonic/gin"
)
func main() {
// 1. Create your Gin engine
r := gin.Default()
// 2. Define your API routes (Gin-MCP will discover these)
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "pong"})
})
r.GET("/users/:id", func(c *gin.Context) {
// Example handler...
userID := c.Param("id")
c.JSON(http.StatusOK, gin.H{"user_id": userID, "status": "fetched"})
})
// 3. Create and configure the MCP server
// Provide essential details for the MCP client.
mcp := server.New(r, &server.Config{
Name: "My Simple API",
Description: "An example API automatically exposed via MCP.",
// BaseURL is crucial! It tells MCP clients where to send requests.
BaseURL: "http://localhost:8080",
})
// 4. Mount the MCP server endpoint
mcp.Mount("/mcp") // MCP clients will connect here
// 5. Run your Gin server
r.Run(":8080") // Gin server runs as usual
}
```
That's it! Your MCP tools are now available at `http://localhost:8080/mcp`. Gin-MCP automatically created tools for `/ping` and `/users/:id`.
> **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.
## Advanced Usage
While Gin-MCP strives for zero configuration, you can customize its behavior.
### Annotating Handlers with Comments
Gin-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:
```go
// listProducts retrieves a paginated list of products
// @summary List all products
// @description Returns a paginated list of products with optional filtering by price, tags, and availability
// @param page Page number for pagination (default: 1)
// @param limit Number of items per page (default: 10, max: 100)
// @param minPrice Minimum price filter
// @param tag Filter products by tag
// @tags public catalog
func listProducts(c *gin.Context) {
// Handler implementation...
}
```
**Supported Annotations:**
- **`@summary`** - Brief one-line description that becomes the tool's primary description
- **`@description`** - Additional detailed explanation appended to the summary
- **`@param `** - Attaches descriptive text to specific input parameters in the generated schema
- **`@tags`** - Space or comma-separated tags used for filtering tools (see "Filtering Exposed Endpoints" below)
- **`@operationId `** - 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.
All annotations are optional, but using them makes your API tools much more user-friendly in MCP clients like Claude Desktop and Cursor.
**Custom Operation IDs:**
By 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:
```go
// getUserProfile retrieves a user's profile with extended metadata
// @summary Get user profile
// @operationId getUserProfile
// @param id User identifier
func getUserProfile(c *gin.Context) {
// Instead of the default "GET_api_v2_users_userId_profile_extended"
// this tool will be named "getUserProfile"
}
```
**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.
### Fine-Grained Schema Control with `RegisterSchema`
Sometimes, 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:
- You use complex structs for query parameters (`ShouldBindQuery`).
- You want to define distinct schemas for request bodies (e.g., for POST/PUT).
- Automatic inference doesn't capture specific constraints (enums, descriptions, etc.) that you want exposed in the MCP tool definition.
```go
package main
import (
// ... other imports
"github.com/ckanthony/gin-mcp/pkg/server"
"github.com/gin-gonic/gin"
)
// Example struct for query parameters
type ListProductsParams struct {
Page int `form:"page,default=1" json:"page,omitempty" jsonschema:"description=Page number,minimum=1"`
Limit int `form:"limit,default=10" json:"limit,omitempty" jsonschema:"description=Items per page,maximum=100"`
Tag string `form:"tag" json:"tag,omitempty" jsonschema:"description=Filter by tag"`
}
// Example struct for POST request body
type CreateProductRequest struct {
Name string `json:"name" jsonschema:"required,description=Product name"`
Price float64 `json:"price" jsonschema:"required,minimum=0,description=Product price"`
}
func main() {
r := gin.Default()
// --- Define Routes ---
r.GET("/products", func(c *gin.Context) { /* ... handler ... */ })
r.POST("/products", func(c *gin.Context) { /* ... handler ... */ })
r.PUT("/products/:id", func(c *gin.Context) { /* ... handler ... */ })
// --- Configure MCP Server ---
mcp := server.New(r, &server.Config{
Name: "Product API",
Description: "API for managing products.",
BaseURL: "http://localhost:8080",
})
// --- Register Schemas ---
// Register ListProductsParams as the query schema for GET /products
mcp.RegisterSchema("GET", "/products", ListProductsParams{}, nil)
// Register CreateProductRequest as the request body schema for POST /products
mcp.RegisterSchema("POST", "/products", nil, CreateProductRequest{})
// You can register schemas for other methods/routes as needed
// e.g., mcp.RegisterSchema("PUT", "/products/:id", nil, UpdateProductRequest{})
mcp.Mount("/mcp")
r.Run(":8080")
}
```
**Explanation:**
- `mcp.RegisterSchema(method, path, querySchema, bodySchema)`
- `method`: HTTP method (e.g., "GET", "POST").
- `path`: Gin route path (e.g., "/products", "/products/:id").
- `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.
- `bodySchema`: An instance of the struct used for the request body (or `nil` if none).
### Filtering Exposed Endpoints
Control 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).
#### Tag-Based Filtering
Tags are specified in handler function comments using the `@tags` annotation. You can specify tags separated by spaces, commas, or both:
```go
// listUsers handles user listing
// @summary List all users
// @tags public users
func listUsers(c *gin.Context) {
// Implementation...
}
// deleteUser handles user deletion
// @summary Delete a user
// @tags admin, internal
func deleteUser(c *gin.Context) {
// Implementation...
}
```
#### Filtering Configuration
```go
// Only include specific operations by their Operation ID
mcp := server.New(r, &server.Config{
// ... other config ...
IncludeOperations: []string{"GET_users", "POST_users"},
})
// Exclude specific operations
mcp := server.New(r, &server.Config{
// ... other config ...
ExcludeOperations: []string{"DELETE_users_id"}, // Don't expose delete tool
})
// Only include operations tagged with "public" or "users"
// A tool is included if it has ANY of the specified tags
mcp := server.New(r, &server.Config{
// ... other config ...
IncludeTags: []string{"public", "users"},
})
// Exclude operations tagged with "admin" or "internal"
// A tool is excluded if it has ANY of the specified tags
mcp := server.New(r, &server.Config{
// ... other config ...
ExcludeTags: []string{"admin", "internal"},
})
```
**Filtering Rules:**
- You can only use **one** inclusion filter (`IncludeOperations` **OR** `IncludeTags`).
- If both are set, `IncludeOperations` takes precedence and a warning is logged.
- You can only use **one** exclusion filter (`ExcludeOperations` **OR** `ExcludeTags`).
- If both are set, `ExcludeOperations` takes precedence and a warning is logged.
- You **can** combine an inclusion filter with an exclusion filter (e.g., include tag "public" but exclude operation "legacyPublicOp").
- **Exclusion always wins**: If a tool matches both inclusion and exclusion filters, it will be excluded.
- **Tag matching**: A tool is included/excluded if it has **any** of the specified tags (OR logic).
**Examples:**
```go
// Include all "public" endpoints but exclude those also tagged "internal"
mcp := server.New(r, &server.Config{
IncludeTags: []string{"public"},
ExcludeTags: []string{"internal"},
})
// Include specific operations but exclude admin endpoints
mcp := server.New(r, &server.Config{
IncludeOperations: []string{"GET_users", "GET_products"},
ExcludeTags: []string{"admin"}, // This will be ignored (precedence rule)
})
```
### Customizing Schema Descriptions (Less Common)
For advanced control over how response schemas are described in the generated tools (often not needed):
```go
mcp := server.New(r, &server.Config{
// ... other config ...
DescribeAllResponses: true, // Include *all* possible response schemas (e.g., 200, 404) in tool descriptions
DescribeFullResponseSchema: true, // Include the full JSON schema object instead of just a reference
})
```
## Examples
See the [`examples`](examples) directory for complete, runnable examples demonstrating various features:
### Basic Usage Examples
- **[`examples/simple/main.go`](examples/simple/main.go)** - Complete product store API with static BaseURL configuration
- **[`examples/simple/quicknode.go`](examples/simple/quicknode.go)** - Dynamic BaseURL configuration for Quicknode proxy environments
- **[`examples/simple/ragflow.go`](examples/simple/ragflow.go)** - Dynamic BaseURL configuration for RAGFlow deployment scenarios
### Dynamic BaseURL for Proxy Scenarios
For environments where each user/deployment has a different endpoint (like Quicknode or RAGFlow), you can configure dynamic BaseURL resolution:
```go
// Quicknode example - resolves user-specific endpoints
mcp := server.New(r, &server.Config{
Name: "Your API",
Description: "API with dynamic Quicknode endpoints",
// No static BaseURL needed!
})
resolver := server.NewQuicknodeResolver("http://localhost:8080")
mcp.SetExecuteToolFunc(func(operationID string, parameters map[string]interface{}) (interface{}, error) {
return mcp.ExecuteToolWithResolver(operationID, parameters, resolver)
})
```
**Environment Variables Supported:**
- **Quicknode**: `QUICKNODE_USER_ENDPOINT`, `USER_ENDPOINT`, `HOST`
- **RAGFlow**: `RAGFLOW_ENDPOINT`, `RAGFLOW_WORKFLOW_URL`, `RAGFLOW_BASE_URL` + `WORKFLOW_ID`
This eliminates the need for static BaseURL configuration at startup, perfect for multi-tenant proxy environments!
## Connecting MCP Clients
Once your Gin application with Gin-MCP is running:
1. Start your application.
2. In your MCP client, provide the URL where you mounted the MCP server (e.g., `http://localhost:8080/mcp`) as the SSE endpoint:
- **Cursor**: Settings → MCP → Add Server
- **Claude Desktop**: Add to MCP configuration file
- **Continue**: Configure in VS Code settings
- **Zed**: Add to MCP settings
3. The client will connect and automatically discover the available API tools.
## Contributing
Contributions are welcome! Please feel free to submit issues or Pull Requests.