The EDteam Go server showcases building MCP servers in Go with the mcp-go library. It integrates with the EDteam API to manage courses, subscriptions, and shopping cart operations.
Features
Go Implementation : Built with mcp-go SDK
API Integration : EDteam educational platform API
Authentication : Token-based authentication flow
Three Tools : Subscriptions, course listing, shopping cart
Environment Configuration : Credential management via env vars
Installation
Prerequisites
Ensure you have Go 1.24.1+ installed:
Set Environment Variables
Create a .env file or export variables:
Build the Server
go build -o edteam-server
Configure MCP Client
Add to your MCP client configuration: {
"mcpServers" : {
"edteam-go" : {
"command" : "/path/to/servers/edteam-go/edteam-server" ,
"env" : {
"EMAIL" : "[email protected] " ,
"PASSWORD" : "your-password"
}
}
}
}
Authentication
The server authenticates on startup using EDteam credentials:
func main () {
log . SetOutput ( os . Stderr )
email := os . Getenv ( "EMAIL" )
password := os . Getenv ( "PASSWORD" )
if email == "" || password == "" {
panic ( "EMAIL and PASSWORD environment variables must be set" )
}
ctx := context . Background ()
token , err := ProcessLogin ( ctx , email , password )
if err != nil {
panic ( err )
}
// Token is now available for all API calls
}
Login Implementation
func ProcessLogin ( ctx context . Context , email , password string ) ( string , error ) {
login := Login {
Email : email ,
Password : password ,
}
// Make the request
urlLogin := "https://api.ed.team/api/v1/login"
statusCode , responseBody , err := Request ( ctx , http . MethodPost , urlLogin , "" , login )
if err != nil {
return "" , err
}
if statusCode != http . StatusOK {
return "" , fmt . Errorf ( "unexpected status code: %d " , statusCode )
}
// Parse the response
var response LoginResponse
err = json . Unmarshal ( responseBody , & response )
if err != nil {
return "" , fmt . Errorf ( "failed to unmarshal response: %w " , err )
}
return response . Data . Token , nil
}
Subscriptions
List all historical subscriptions from your EDteam account.
Parameters: None
Example Response:
{
"data" : [
{
"id" : 12345 ,
"plan_name" : "Premium" ,
"start_date" : "2024-01-01" ,
"end_date" : "2024-12-31" ,
"status" : "active"
}
]
}
Implementation:
subscriptionsTool := mcp . NewTool (
"Subscriptions" ,
mcp . WithDescription ( "List all your subscriptions in the history of EDteam" ),
)
s . AddTool ( subscriptionsTool , func ( ctx context . Context , req mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
subscriptions , err := GetSubscription ( ctx , token )
if err != nil {
return nil , err
}
var subscriptionsRaw [] byte
subscriptionsRaw , err = json . Marshal ( subscriptions )
if err != nil {
return nil , err
}
return mcp . NewToolResultText ( string ( subscriptionsRaw )), nil
})
API Function:
func GetSubscription ( ctx context . Context , token string ) ( SubscriptionResponse , error ) {
urlSubscriptions := "https://api.ed.team/api/v1/subscriptions/historical"
statusCode , responseBody , err := Request ( ctx , http . MethodGet , urlSubscriptions , token , nil )
if err != nil {
return SubscriptionResponse {}, err
}
if statusCode != http . StatusOK {
return SubscriptionResponse {}, fmt . Errorf ( "unexpected status code: %d " , statusCode )
}
// Parse the response
var subscriptions SubscriptionResponse
err = json . Unmarshal ( responseBody , & subscriptions )
if err != nil {
return SubscriptionResponse {}, fmt . Errorf ( "failed to unmarshal response: %w " , err )
}
return subscriptions , nil
}
Courses-List
List available courses from EDteam with pagination.
Parameters:
page (number, optional): Page number (default: 1)
limit (number, optional): Number of courses per page (1-10, default: 10)
Example:
{
"page" : 2 ,
"limit" : 5
}
Implementation:
coursesListTool := mcp . NewTool (
"Courses-List" ,
mcp . WithDescription ( "List all courses of EDteam" ),
mcp . WithNumber ( "page" , mcp . Description ( "Page number" ), mcp . DefaultNumber ( 1 )),
mcp . WithNumber ( "limit" , mcp . Description ( "Limit number of courses" ), mcp . DefaultNumber ( 10 )),
)
s . AddTool ( coursesListTool , func ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
page , ok := request . Params . Arguments [ "page" ].( float64 )
if ! ok {
page = 1
}
limit , ok := request . Params . Arguments [ "limit" ].( float64 )
if ! ok || limit <= 0 || limit > 10 {
limit = 10
}
courses , err := GetCourses ( ctx , uint ( page ), uint ( limit ))
if err != nil {
return nil , err
}
var coursesRaw [] byte
coursesRaw , err = json . Marshal ( courses )
if err != nil {
return nil , err
}
return mcp . NewToolResultText ( string ( coursesRaw )), nil
})
Add a course to your shopping cart.
Parameters:
course_id (number, required): The course ID to add
Example:
Implementation:
shoppingCartTool := mcp . NewTool (
"Shopping-Cart-Add-Course" ,
mcp . WithDescription ( "Add a course to your shopping cart" ),
mcp . WithNumber ( "course_id" , mcp . Description ( "Course ID" ), mcp . DefaultNumber ( 0 ), mcp . Required ()),
)
s . AddTool ( shoppingCartTool , func ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
courseID , ok := request . Params . Arguments [ "course_id" ].( float64 )
if ! ok {
return nil , fmt . Errorf ( "course_id must be a number" )
}
shoppingCart , err := AddCourseToShoppingCart ( ctx , token , int ( courseID ))
if err != nil {
return nil , err
}
var shoppingCartRaw [] byte
shoppingCartRaw , err = json . Marshal ( shoppingCart )
if err != nil {
return nil , err
}
return mcp . NewToolResultText ( string ( shoppingCartRaw )), nil
})
Server Configuration
import (
" context "
" encoding/json "
" fmt "
" log "
" os "
" github.com/mark3labs/mcp-go/mcp "
" github.com/mark3labs/mcp-go/server "
)
func main () {
log . SetOutput ( os . Stderr )
// Authenticate
email := os . Getenv ( "EMAIL" )
password := os . Getenv ( "PASSWORD" )
if email == "" || password == "" {
panic ( "EMAIL and PASSWORD environment variables must be set" )
}
ctx := context . Background ()
token , err := ProcessLogin ( ctx , email , password )
if err != nil {
panic ( err )
}
// Create MCP server
s := server . NewMCPServer (
"EDteam API" ,
"1.0.0" ,
server . WithToolCapabilities ( false ),
server . WithLogging (),
)
// Add tools...
// Start server
if err := server . ServeStdio ( s ); err != nil {
panic ( err )
}
}
HTTP Request Helper
Generic HTTP request function used by all API calls:
func Request ( ctx context . Context , method , url , token string , body interface {}) ( int , [] byte , error ) {
var reqBody io . Reader
if body != nil {
jsonData , err := json . Marshal ( body )
if err != nil {
return 0 , nil , fmt . Errorf ( "failed to marshal request body: %w " , err )
}
reqBody = bytes . NewBuffer ( jsonData )
}
req , err := http . NewRequestWithContext ( ctx , method , url , reqBody )
if err != nil {
return 0 , nil , fmt . Errorf ( "failed to create request: %w " , err )
}
req . Header . Set ( "Content-Type" , "application/json" )
if token != "" {
req . Header . Set ( "Authorization" , fmt . Sprintf ( "Bearer %s " , token ))
}
client := & http . Client {}
resp , err := client . Do ( req )
if err != nil {
return 0 , nil , fmt . Errorf ( "failed to send request: %w " , err )
}
defer resp . Body . Close ()
responseBody , err := io . ReadAll ( resp . Body )
if err != nil {
return resp . StatusCode , nil , fmt . Errorf ( "failed to read response body: %w " , err )
}
return resp . StatusCode , responseBody , nil
}
Usage Examples
List Your Subscriptions
User: Show me my EDteam subscriptions
Assistant: [Uses Subscriptions tool]
You have 2 subscriptions:
1. Premium Plan - Active (Jan 2024 - Dec 2024)
2. Basic Plan - Expired (Jan 2023 - Dec 2023)
Browse Courses
User: Show me 5 courses from EDteam
Assistant: [Uses Courses-List with page=1, limit=5]
Here are 5 courses:
1. Introduction to Go Programming
2. Advanced TypeScript Patterns
3. Building REST APIs
4. Database Design Fundamentals
5. DevOps with Docker
Add to Cart
User: Add course 42 to my shopping cart
Assistant: [Uses Shopping-Cart-Add-Course with course_id=42]
Course added to your shopping cart successfully!
Dependencies
go.mod:
module edteam - mcp
go 1.24.1
require github . com / mark3labs / mcp - go v0 . 18.0
require (
github . com / google / uuid v1 . 6.0 // indirect
github . com / yosida95 / uritemplate / v3 v3 . 0.2 // indirect
)
Key Packages:
github.com/mark3labs/mcp-go - MCP SDK for Go
github.com/google/uuid - UUID generation
github.com/yosida95/uritemplate/v3 - URI template parsing
Project Structure
edteam-go/
├── main.go # Server initialization and tool registration
├── login.go # Authentication logic
├── subscription.go # Subscription API calls
├── courses.go # Course listing API
├── shopping-cart.go # Shopping cart operations
├── models.go # Data structures
├── http.go # HTTP request helper
├── go.mod # Module definition
└── go.sum # Dependency checksums
Security Considerations
Never commit credentials to version control . Use environment variables or secure credential management systems.
Environment Variables Store credentials in .env files (gitignored) or MCP client config
Token Security Authentication token is kept in memory only during server runtime
HTTPS Only All API calls use HTTPS to the EDteam API
Error Handling Avoid logging sensitive data in error messages
Extending the Server
Add New API Endpoint
// 1. Create API function
func GetUserProfile ( ctx context . Context , token string ) ( ProfileResponse , error ) {
url := "https://api.ed.team/api/v1/profile"
statusCode , responseBody , err := Request ( ctx , http . MethodGet , url , token , nil )
if err != nil {
return ProfileResponse {}, err
}
if statusCode != http . StatusOK {
return ProfileResponse {}, fmt . Errorf ( "unexpected status code: %d " , statusCode )
}
var profile ProfileResponse
err = json . Unmarshal ( responseBody , & profile )
return profile , err
}
// 2. Register as MCP tool
profileTool := mcp . NewTool (
"User-Profile" ,
mcp . WithDescription ( "Get your EDteam user profile" ),
)
s . AddTool ( profileTool , func ( ctx context . Context , req mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
profile , err := GetUserProfile ( ctx , token )
if err != nil {
return nil , err
}
profileRaw , err := json . Marshal ( profile )
if err != nil {
return nil , err
}
return mcp . NewToolResultText ( string ( profileRaw )), nil
})
Troubleshooting
Verify your EMAIL and PASSWORD environment variables are correct: echo $EMAIL
echo $PASSWORD
Test login manually via the EDteam website to confirm credentials.
This server requires Go 1.24.1+: Update Go from go.dev/dl if needed.
Clean and reinstall dependencies: go clean -modcache
go mod download
go build
The EDteam API may have rate limits. Implement exponential backoff for production use: func RequestWithRetry ( ctx context . Context , method , url , token string , body interface {}) ( int , [] byte , error ) {
maxRetries := 3
for i := 0 ; i < maxRetries ; i ++ {
status , resp , err := Request ( ctx , method , url , token , body )
if status != http . StatusTooManyRequests {
return status , resp , err
}
time . Sleep ( time . Second * time . Duration ( math . Pow ( 2 , float64 ( i ))))
}
return 0 , nil , fmt . Errorf ( "max retries exceeded" )
}
Go MCP Patterns
// Type assertion with defaults
page , ok := request . Params . Arguments [ "page" ].( float64 )
if ! ok {
page = 1
}
// Required parameters
courseID , ok := request . Params . Arguments [ "course_id" ].( float64 )
if ! ok {
return nil , fmt . Errorf ( "course_id is required and must be a number" )
}
Error Handling
// Return errors from tool handlers
if err != nil {
return nil , fmt . Errorf ( "failed to fetch courses: %w " , err )
}
// Server-level panics for fatal errors
if token == "" {
panic ( "authentication failed" )
}
Context Usage
// Pass context through all API calls
func GetCourses ( ctx context . Context , page , limit uint ) ( CoursesResponse , error ) {
// Use ctx for cancellation and timeouts
req , err := http . NewRequestWithContext ( ctx , method , url , body )
// ...
}
Next Steps
Basic TypeScript Explore prompts and resources with TypeScript
Calculator Python See how FastMCP simplifies Python server development