diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..24454f6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,68 @@ +--- +name: Bug Report +about: Create a report to help us improve gomcp +title: '[BUG] ' +labels: 'type: bug, status: needs-triage' +assignees: '' +--- + +## Bug Description + +A clear and concise description of what the bug is. + +## Environment + +- **gomcp version**: [e.g., 0.1.0] +- **Go version**: [e.g., 1.23.0] +- **OS**: [e.g., macOS 14.0, Ubuntu 22.04] +- **Transport**: [e.g., HTTP/SSE, stdio] +- **Client**: [e.g., Cursor IDE, Claude Desktop] + +## Steps to Reproduce + +1. Go to '...' +2. Run command '...' +3. Configure '...' +4. See error + +## Expected Behavior + +A clear and concise description of what you expected to happen. + +## Actual Behavior + +A clear and concise description of what actually happened. + +## Logs/Error Output + +``` +Paste any relevant logs or error messages here +``` + +## Configuration + +```env +# Relevant environment variables (redact secrets!) +MCP_TRANSPORT_PROTOCOL= +MCP_PORT= +CURSOR_COMPATIBLE_SSE= +``` + +## Screenshots + +If applicable, add screenshots to help explain your problem. + +## Additional Context + +Add any other context about the problem here. + +## Possible Solution + +If you have ideas on how to fix this, please share them here. + +## Checklist + +- [ ] I have searched existing issues to ensure this is not a duplicate +- [ ] I have included all relevant environment information +- [ ] I have provided steps to reproduce the issue +- [ ] I have included relevant logs/error messages diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..c0f874e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: Question / Discussion + url: https://github.com/NP-compete/gomcp/discussions + about: Ask questions and discuss ideas in GitHub Discussions + - name: MCP Specification + url: https://modelcontextprotocol.io/specification/2025-06-18 + about: Reference the official MCP specification + - name: Official Go SDK + url: https://github.com/modelcontextprotocol/go-sdk + about: Check the official MCP Go SDK documentation diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..1e0113f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,58 @@ +--- +name: Feature Request +about: Suggest an idea for gomcp +title: '[FEATURE] ' +labels: 'type: feature, status: needs-triage' +assignees: '' +--- + +## Feature Description + +A clear and concise description of the feature you'd like to see. + +## Problem Statement + +Is your feature request related to a problem? Please describe. +Example: "I'm always frustrated when [...]" + +## Proposed Solution + +A clear and concise description of what you want to happen. + +## Use Case + +Describe the use case(s) this feature would enable: + +1. As a [type of user], I want [goal] so that [benefit] +2. ... + +## Alternatives Considered + +A clear and concise description of any alternative solutions or features you've considered. + +## MCP Specification Alignment + +Does this feature align with the MCP specification? + +- [ ] This is part of the MCP spec (link to relevant section) +- [ ] This extends beyond the MCP spec (explain why it's valuable) +- [ ] Not applicable + +## Implementation Ideas + +If you have ideas on how to implement this, please share: + +```go +// Example code or pseudocode +``` + +## Additional Context + +Add any other context, mockups, or screenshots about the feature request here. + +## Checklist + +- [ ] I have searched existing issues/discussions to ensure this is not a duplicate +- [ ] I have clearly described the problem this feature would solve +- [ ] I have considered alternatives +- [ ] I am willing to help implement this feature (optional) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..980a345 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,88 @@ +## Description + + + +## Type of Change + + + +- [ ] Bug fix (non-breaking change that fixes an issue) +- [ ] New feature (non-breaking change that adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] Documentation update +- [ ] Refactoring (no functional changes) +- [ ] CI/CD changes +- [ ] Other (please describe): + +## Related Issues + + + +Fixes # + +## Changes Made + + + +- +- +- + +## MCP Features Affected + + + +- [ ] Tools +- [ ] Prompts +- [ ] Resources +- [ ] Roots +- [ ] Completion +- [ ] Logging +- [ ] Pagination +- [ ] Sampling +- [ ] Elicitation +- [ ] Progress +- [ ] Cancellation +- [ ] Transport (HTTP/SSE/stdio) +- [ ] None of the above + +## Testing + + + +### Test Commands Run + +```bash +make test +make lint +``` + +### Manual Testing + + + +- [ ] Tested with Cursor IDE +- [ ] Tested with Claude Desktop +- [ ] Tested with HTTP client +- [ ] N/A + +## Screenshots + + + +## Checklist + + + +- [ ] My code follows the project's style guidelines +- [ ] I have performed a self-review of my code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] My changes generate no new warnings +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] New and existing unit tests pass locally with my changes +- [ ] Any dependent changes have been merged and published + +## Additional Notes + + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7869f21 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,157 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +env: + GO_VERSION: '1.24' + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + cache: true + + - name: Run go vet + run: go vet ./... + + - name: Run go fmt check + run: | + if [ -n "$(gofmt -l .)" ]; then + echo "The following files are not formatted:" + gofmt -l . + exit 1 + fi + + - name: Install golangci-lint + uses: golangci/golangci-lint-action@v4 + with: + version: latest + args: --timeout=5m + + test: + name: Test + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + cache: true + + - name: Download dependencies + run: go mod download + + - name: Run tests + run: go test -v -race -coverprofile=coverage.out -covermode=atomic ./... + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + files: ./coverage.out + flags: unittests + name: codecov-umbrella + fail_ci_if_error: false + + build: + name: Build + runs-on: ubuntu-latest + needs: [lint, test] + strategy: + matrix: + goos: [linux, darwin, windows] + goarch: [amd64, arm64] + exclude: + - goos: windows + goarch: arm64 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + cache: true + + - name: Build binary + env: + GOOS: ${{ matrix.goos }} + GOARCH: ${{ matrix.goarch }} + CGO_ENABLED: 0 + run: | + VERSION=$(cat VERSION) + GIT_COMMIT=$(git rev-parse --short HEAD) + BUILD_TIME=$(date -u '+%Y-%m-%d_%H:%M:%S') + + BINARY_NAME=gomcp + if [ "$GOOS" = "windows" ]; then + BINARY_NAME=gomcp.exe + fi + + go build \ + -ldflags "-X github.com/NP-compete/gomcp/internal/version.Version=${VERSION} \ + -X github.com/NP-compete/gomcp/internal/version.GitCommit=${GIT_COMMIT} \ + -X github.com/NP-compete/gomcp/internal/version.BuildTime=${BUILD_TIME}" \ + -trimpath \ + -o bin/${GOOS}-${GOARCH}/${BINARY_NAME} \ + ./cmd/server + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: gomcp-${{ matrix.goos }}-${{ matrix.goarch }} + path: bin/${{ matrix.goos }}-${{ matrix.goarch }}/ + retention-days: 7 + + docker: + name: Docker Build + runs-on: ubuntu-latest + needs: [lint, test] + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: false + tags: gomcp-server:${{ github.sha }} + cache-from: type=gha + cache-to: type=gha,mode=max + + security: + name: Security Scan + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + cache: true + + - name: Run govulncheck + run: | + go install golang.org/x/vuln/cmd/govulncheck@latest + govulncheck ./... diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..b53f730 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,195 @@ +name: Release + +on: + push: + tags: + - 'v*' + +env: + GO_VERSION: '1.24' + +permissions: + contents: write + packages: write + +jobs: + release: + name: Create Release + runs-on: ubuntu-latest + outputs: + upload_url: ${{ steps.create_release.outputs.upload_url }} + version: ${{ steps.get_version.outputs.version }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Get version + id: get_version + run: echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT + + - name: Generate changelog + id: changelog + run: | + # Get commits since last tag + PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "") + if [ -n "$PREVIOUS_TAG" ]; then + CHANGELOG=$(git log --pretty=format:"- %s (%h)" ${PREVIOUS_TAG}..HEAD) + else + CHANGELOG=$(git log --pretty=format:"- %s (%h)") + fi + echo "changelog<> $GITHUB_OUTPUT + echo "$CHANGELOG" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ steps.get_version.outputs.version }} + body: | + ## gomcp v${{ steps.get_version.outputs.version }} + + ### Changes + ${{ steps.changelog.outputs.changelog }} + + ### Installation + + #### Binary Download + Download the appropriate binary for your platform from the assets below. + + #### Docker + ```bash + docker pull ghcr.io/np-compete/gomcp:${{ steps.get_version.outputs.version }} + ``` + + #### From Source + ```bash + git clone https://github.com/NP-compete/gomcp.git + cd gomcp + git checkout v${{ steps.get_version.outputs.version }} + make build-prod + ``` + + See [CHANGELOG.md](https://github.com/NP-compete/gomcp/blob/main/CHANGELOG.md) for full details. + draft: false + prerelease: ${{ contains(github.ref, '-rc') || contains(github.ref, '-beta') || contains(github.ref, '-alpha') }} + + build: + name: Build Binaries + runs-on: ubuntu-latest + needs: release + strategy: + matrix: + goos: [linux, darwin, windows] + goarch: [amd64, arm64] + exclude: + - goos: windows + goarch: arm64 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + cache: true + + - name: Build binary + env: + GOOS: ${{ matrix.goos }} + GOARCH: ${{ matrix.goarch }} + CGO_ENABLED: 0 + run: | + VERSION=${{ needs.release.outputs.version }} + GIT_COMMIT=$(git rev-parse --short HEAD) + BUILD_TIME=$(date -u '+%Y-%m-%d_%H:%M:%S') + + BINARY_NAME=gomcp + if [ "$GOOS" = "windows" ]; then + BINARY_NAME=gomcp.exe + fi + + go build \ + -ldflags "-X github.com/NP-compete/gomcp/internal/version.Version=${VERSION} \ + -X github.com/NP-compete/gomcp/internal/version.GitCommit=${GIT_COMMIT} \ + -X github.com/NP-compete/gomcp/internal/version.BuildTime=${BUILD_TIME}" \ + -trimpath \ + -o ${BINARY_NAME} \ + ./cmd/server + + - name: Create archive + run: | + BINARY_NAME=gomcp + ARCHIVE_NAME=gomcp-${{ needs.release.outputs.version }}-${{ matrix.goos }}-${{ matrix.goarch }} + + if [ "${{ matrix.goos }}" = "windows" ]; then + BINARY_NAME=gomcp.exe + zip ${ARCHIVE_NAME}.zip ${BINARY_NAME} README.md LICENSE + echo "ASSET_PATH=${ARCHIVE_NAME}.zip" >> $GITHUB_ENV + echo "ASSET_NAME=${ARCHIVE_NAME}.zip" >> $GITHUB_ENV + echo "ASSET_TYPE=application/zip" >> $GITHUB_ENV + else + tar -czvf ${ARCHIVE_NAME}.tar.gz ${BINARY_NAME} README.md LICENSE + echo "ASSET_PATH=${ARCHIVE_NAME}.tar.gz" >> $GITHUB_ENV + echo "ASSET_NAME=${ARCHIVE_NAME}.tar.gz" >> $GITHUB_ENV + echo "ASSET_TYPE=application/gzip" >> $GITHUB_ENV + fi + + - name: Upload Release Asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.release.outputs.upload_url }} + asset_path: ${{ env.ASSET_PATH }} + asset_name: ${{ env.ASSET_NAME }} + asset_content_type: ${{ env.ASSET_TYPE }} + + docker: + name: Build and Push Docker Image + runs-on: ubuntu-latest + needs: release + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/np-compete/gomcp + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..44b4869 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,19 @@ +run: + timeout: 5m + modules-download-mode: readonly + +linters: + disable: + - errcheck + +linters-settings: + govet: + enable-all: true + disable: + - fieldalignment + - shadow + - unusedwrite + +issues: + max-issues-per-linter: 0 + max-same-issues: 0 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..8e889ef --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,133 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[INSERT CONTACT METHOD]. + +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..298b16c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,247 @@ +# Contributing to gomcp + +First off, thank you for considering contributing to gomcp! It's people like you that make gomcp such a great tool for the MCP community. + +## Code of Conduct + +This project and everyone participating in it is governed by our [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. + +## How Can I Contribute? + +### Reporting Bugs + +Before creating bug reports, please check the existing issues to avoid duplicates. When you create a bug report, include as many details as possible using our [bug report template](.github/ISSUE_TEMPLATE/bug_report.md). + +**Great Bug Reports** tend to have: + +- A quick summary and/or background +- Steps to reproduce (be specific!) +- What you expected would happen +- What actually happens +- Notes (possibly including why you think this might be happening) + +### Suggesting Enhancements + +Enhancement suggestions are tracked as GitHub issues. When creating an enhancement suggestion, use our [feature request template](.github/ISSUE_TEMPLATE/feature_request.md) and include: + +- A clear and descriptive title +- A detailed description of the proposed enhancement +- Explain why this enhancement would be useful +- List any alternatives you've considered + +### Pull Requests + +1. Fork the repo and create your branch from `main` +2. If you've added code that should be tested, add tests +3. If you've changed APIs, update the documentation +4. Ensure the test suite passes +5. Make sure your code lints +6. Issue that pull request! + +## Development Setup + +### Prerequisites + +- Go 1.23 or higher +- Docker/Podman (optional, for containerized development) + +### Getting Started + +```bash +# Clone your fork +git clone https://github.com/YOUR_USERNAME/gomcp.git +cd gomcp + +# Add upstream remote +git remote add upstream https://github.com/NP-compete/gomcp.git + +# Install dependencies +go mod download + +# Install development tools +make tools + +# Run tests +make test + +# Run linter +make lint +``` + +### Development Workflow + +```bash +# Create a feature branch +git checkout -b feature/my-new-feature + +# Make your changes and test +make test +make lint + +# Run the server locally +make run + +# For Cursor IDE testing +make cursor +``` + +### Project Structure + +``` +gomcp/ +├── cmd/server/ # Application entry point +├── internal/ +│ ├── api/ # HTTP handlers & routing +│ ├── completion/ # Structured outputs +│ ├── config/ # Configuration management +│ ├── logging/ # Server-to-client logs +│ ├── mcp/ # MCP server logic +│ ├── pagination/ # Cursor-based pagination +│ ├── prompts/ # Prompt implementations +│ ├── resources/ # Resource implementations +│ ├── roots/ # Filesystem roots +│ └── tools/ # Tool implementations +├── pkg/mcpprotocol/ # MCP protocol types +├── test/ # Integration tests +└── docs/ # Documentation +``` + +## Adding New Features + +### Adding a New Tool + +1. Create `internal/tools/mytool_sdk.go`: + +```go +package tools + +import ( + "context" + "github.com/modelcontextprotocol/go-sdk/mcp" +) + +type MyToolInput struct { + Param string `json:"param" jsonschema:"required,description=Parameter description"` +} + +type MyToolOutput struct { + Result string `json:"result"` +} + +func MyToolSDK(ctx context.Context, req *mcp.CallToolRequest, input MyToolInput) (*mcp.CallToolResult, MyToolOutput, error) { + output := MyToolOutput{Result: "success"} + return nil, output, nil +} +``` + +2. Register in `internal/mcp/server_sdk.go`: + +```go +mcp.AddTool( + server, + &mcp.Tool{ + Name: "my_tool", + Description: "Description of my tool", + }, + tools.MyToolSDK, +) +``` + +3. Add tests in `internal/tools/mytool_test.go` + +### Adding a New Prompt + +1. Create `internal/prompts/myprompt.go` +2. Register in `internal/mcp/server_sdk.go` +3. Add tests + +### Adding a New Resource + +1. Create `internal/resources/myresource.go` +2. Register in `internal/mcp/server_sdk.go` +3. Add tests + +## Style Guidelines + +### Go Code Style + +- Follow [Effective Go](https://golang.org/doc/effective_go.html) +- Use `gofmt` for formatting (run `make lint`) +- Write meaningful comments for exported functions +- Keep functions focused and small +- Handle errors explicitly + +### Commit Messages + +We follow [Conventional Commits](https://www.conventionalcommits.org/): + +``` +(): + +[optional body] + +[optional footer(s)] +``` + +Types: +- `feat`: New feature +- `fix`: Bug fix +- `docs`: Documentation only +- `style`: Formatting, missing semicolons, etc. +- `refactor`: Code change that neither fixes a bug nor adds a feature +- `test`: Adding missing tests +- `chore`: Maintenance tasks + +Examples: +``` +feat(tools): add weather lookup tool +fix(transport): handle SSE reconnection properly +docs(readme): add Cursor IDE setup instructions +``` + +### Testing + +- Write tests for new functionality +- Maintain or improve code coverage +- Use table-driven tests where appropriate +- Mock external dependencies + +```bash +# Run all tests +make test + +# Run specific tests +go test -v ./internal/tools/... + +# Run with coverage +go test -coverprofile=coverage.out ./... +go tool cover -html=coverage.out +``` + +## Review Process + +1. All submissions require review +2. We use GitHub pull requests for this purpose +3. Reviewers will look for: + - Code quality and style + - Test coverage + - Documentation updates + - Backward compatibility + +## Community + +- Use GitHub Discussions for questions and ideas +- Be respectful and constructive +- Help others when you can + +## Recognition + +Contributors are recognized in our release notes and README. Thank you for making gomcp better! + +## Questions? + +Feel free to open an issue with the `question` label or start a discussion. + +--- + +Thank you for contributing! diff --git a/Dockerfile b/Dockerfile index 83fd10a..84b1c37 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Build stage -FROM golang:1.23-alpine AS builder +FROM golang:1.24-alpine AS builder # Install build dependencies RUN apk add --no-cache git ca-certificates diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..98a2be0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 NP-compete + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 677f146..d0a03d0 100644 --- a/README.md +++ b/README.md @@ -1,75 +1,126 @@ -# gomcp - Go MCP Server Template +

+ MCP Version + Go Version + License + Release +

+ +

+ + CI + + + Coverage + + + Go Report Card + +

+ +

gomcp

+ +

+ Production-ready Model Context Protocol (MCP) server template in Go +

+ +

+ Features | + Quick Start | + Use as Template | + Documentation | + Contributing +

-Production-ready Model Context Protocol (MCP) server template in Go with **complete MCP 2025-06-18 support**. +--- + +## Overview + +**gomcp** is a complete, production-ready MCP server template built with Go and the [official MCP Go SDK](https://github.com/modelcontextprotocol/go-sdk). It implements the full [MCP 2025-06-18 specification](https://modelcontextprotocol.io/specification/2025-06-18) and is designed to be used as a starting point for building your own MCP servers. + +### Why gomcp? + +- **Complete Implementation** - All 12 MCP features fully implemented +- **Production Ready** - Docker support, CI/CD, security best practices +- **Well Documented** - Comprehensive docs, examples, and customization guide +- **Easy to Customize** - Clean architecture, modular design, extensive comments +- **Multiple Transports** - HTTP/SSE for Cursor IDE, stdio for Claude Desktop + +## Features + + + + + + +
+ +### MCP Features (12/12) -## ✨ Features +| Feature | Status | Description | +|---------|:------:|-------------| +| Tools | Yes | 4 example tools with structured outputs | +| Prompts | Yes | 3 reusable prompt templates | +| Resources | Yes | 6 static/dynamic resources | +| Roots | Yes | Filesystem root definitions | +| Completion | Yes | Structured outputs with JSON schemas | +| Logging | Yes | Server-to-client notifications (8 levels) | +| Pagination | Yes | Cursor-based pagination | +| Sampling | Yes | Server-to-client LLM requests | +| Elicitation | Yes | Server-to-user data requests | +| Progress | Yes | Real-time progress notifications | +| Cancellation | Yes | Request cancellation support | +| Ping | Yes | Health checks | -**12 MCP Features Fully Implemented:** + + +### Infrastructure | Feature | Description | |---------|-------------| -| **Tools** | 4 example tools with structured outputs | -| **Prompts** | 3 reusable prompt templates | -| **Resources** | 6 static/dynamic resources | -| **Roots** | Filesystem root definitions | -| **Completion** | Structured tool outputs with JSON schemas | -| **Logging** | Server→client log notifications (8 levels) | -| **Pagination** | Cursor-based pagination (max 100/page) | -| **Sampling** | Server→client LLM requests | -| **Elicitation** | Server→user data requests | -| **Progress** | Real-time progress notifications | -| **Cancellation** | Request cancellation support | -| **Ping** | Health checks | - -## 🚀 Quick Start +| **Multi-Transport** | HTTP/SSE + stdio support | +| **Cursor Compatible** | First-class Cursor IDE support | +| **Claude Desktop** | stdio transport for local use | +| **Docker Ready** | Multi-stage Dockerfile included | +| **CI/CD** | GitHub Actions workflows | +| **Hot Reload** | Development mode with air | +| **OAuth Support** | Authentication ready | +| **Metrics** | Built-in monitoring endpoints | +| **Security** | Non-root containers, SSL/TLS | + +
+ +## Quick Start ### Prerequisites -- Go 1.23+ +- Go 1.24+ - (Optional) Docker/Podman -**Important:** Ensure Go's bin directory is in your PATH: - -```bash -# Add to PATH (required for air, development tools) -export PATH=$PATH:$(go env GOPATH)/bin - -# Make it permanent (add to ~/.zshrc or ~/.bashrc): -echo 'export PATH=$PATH:$(go env GOPATH)/bin' >> ~/.zshrc -source ~/.zshrc -``` - -### 1. Clone & Install +### Installation ```bash +# Clone the repository git clone https://github.com/NP-compete/gomcp.git cd gomcp + +# Download dependencies go mod download + +# Build and run +make run ``` -### 2. Run Server +### Running the Server + +
+For Cursor IDE (HTTP/SSE) -**For Cursor IDE:** ```bash make cursor # Server runs on http://localhost:8081/mcp/sse ``` -**For Claude Desktop:** -```bash -export MCP_TRANSPORT_PROTOCOL=stdio -go run cmd/server/main.go -``` - -**Default (HTTP):** -```bash -make run -# Server runs on http://localhost:8081 -``` - -### 3. Configure Client - -**Cursor IDE** (`~/.cursor/mcp.json`): +Configure `~/.cursor/mcp.json`: ```json { "mcpServers": { @@ -80,12 +131,27 @@ make run } ``` -**Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json`): +Restart Cursor IDE to connect. + +
+ +
+For Claude Desktop (stdio) + +```bash +# Build the binary +make build-prod + +# Configure Claude Desktop +# ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) +# %APPDATA%\Claude\claude_desktop_config.json (Windows) +``` + ```json { "mcpServers": { "gomcp": { - "command": "/path/to/gomcp/bin/gomcp", + "command": "/absolute/path/to/gomcp/bin/gomcp", "args": [], "env": { "MCP_TRANSPORT_PROTOCOL": "stdio" @@ -95,262 +161,152 @@ make run } ``` -## 📦 What's Included +Restart Claude Desktop to connect. -### Example Tools -- `multiply_numbers` - Number multiplication with structured output -- `code_review` - Generate code review analysis -- `logo` - Display server logo -- `long_operation` - Demonstrates progress & cancellation +
-### Example Prompts -- `code_review` - Code review template -- `git_commit` - Git commit message generator -- `debug_help` - Debugging assistance +
+With Docker -### Example Resources -- `project://info` - Server information -- `project://status` - System status -- `docs://quickstart` - Quick start guide -- `docs://api-reference` - API documentation -- `config://template` - Configuration template -- `config://env-vars` - Environment variables +```bash +# Build and run with Docker +make docker-build +make docker-run -## 🔧 Configuration +# Or with Docker Compose +docker-compose up -d +``` -**Environment Variables:** +
-| Variable | Default | Description | -|----------|---------|-------------| -| `MCP_TRANSPORT_PROTOCOL` | `http` | Transport: `stdio`, `http`, `sse` | -| `MCP_PORT` | `8081` | Server port | -| `CURSOR_COMPATIBLE_SSE` | `true` | Enable Cursor compatibility | -| `ENABLE_AUTH` | `true` | Enable OAuth authentication | -| `LOG_LEVEL` | `INFO` | Log level | +## Use as Template -Create `.env` file: -```bash -MCP_TRANSPORT_PROTOCOL=http -MCP_PORT=8081 -CURSOR_COMPATIBLE_SSE=true -ENABLE_AUTH=false -``` +This repository is designed to be used as a template for your own MCP server. -## 🧪 Testing +### Option 1: GitHub Template (Recommended) -```bash -# Run all tests -./scripts/test_all.sh +Click the **"Use this template"** button on GitHub to create a new repository. -# Run specific tests -go test -v ./internal/completion -go test -v ./internal/logging -go test -v ./internal/pagination +### Option 2: Manual Clone -# Generate coverage -go test -coverprofile=coverage.out ./... -go tool cover -html=coverage.out -o coverage.html +```bash +# Clone and remove git history +git clone https://github.com/NP-compete/gomcp.git my-mcp-server +cd my-mcp-server +rm -rf .git +git init + +# Update module name +# Edit go.mod: module github.com/YOUR_USERNAME/my-mcp-server +# Then update all imports ``` -**Test Coverage:** 65 tests, 100% pass rate +### Customization Guide -## 📁 Project Structure +See [docs/CUSTOMIZATION.md](docs/CUSTOMIZATION.md) for detailed instructions on: + +- Adding your own tools, prompts, and resources +- Configuring authentication +- Customizing the Docker setup +- Setting up CI/CD for your repository + +## Project Structure ``` gomcp/ -├── cmd/server/ # Main application entry point +├── cmd/server/ # Application entry point ├── internal/ │ ├── api/ # HTTP handlers & routing │ ├── completion/ # Structured outputs -│ ├── logging/ # Server→client logs +│ ├── config/ # Configuration management +│ ├── logging/ # Server-to-client logs +│ ├── mcp/ # MCP server logic │ ├── pagination/ # Cursor-based pagination │ ├── prompts/ # Prompt implementations │ ├── resources/ # Resource implementations │ ├── roots/ # Filesystem roots -│ ├── tools/ # Tool implementations -│ ├── mcp/ # MCP server logic -│ └── config/ # Configuration management -├── pkg/mcpprotocol/ # MCP protocol implementation +│ └── tools/ # Tool implementations +├── pkg/mcpprotocol/ # MCP protocol types ├── test/ # Integration tests -└── Makefile # Build & run commands -``` - -## 🛠️ Development - -### Hot Reload -```bash -make dev # Auto-restarts on file changes -``` - -### Build Commands -```bash -make build # Development build -make build-prod # Production build with optimization -make clean # Clean artifacts -make deps # Update dependencies +├── docs/ # Documentation +├── scripts/ # Utility scripts +├── .github/ # GitHub Actions & templates +├── Dockerfile # Container build +├── docker-compose.yml # Local development +└── Makefile # Build commands ``` -### Docker -```bash -make docker-build # Build image -make docker-run # Run container -``` - -## 🎯 Adding Features - -### 1. Add a Tool - -Create `internal/tools/mytool_sdk.go`: -```go -type MyToolInput struct { - Param string `json:"param" jsonschema:"required,parameter description"` -} - -type MyToolOutput struct { - Result string `json:"result"` -} - -func MyTool(ctx context.Context, req *mcp.CallToolRequest, input MyToolInput) (*mcp.CallToolResult, MyToolOutput, error) { - // Your logic here - output := MyToolOutput{Result: "success"} - return nil, output, nil -} -``` - -Register in `internal/mcp/server_sdk.go`: -```go -server.AddTool(mcp.NewTool("mytool", "Description", MyTool)) -``` - -### 2. Add a Prompt - -Create `internal/prompts/myprompt.go`: -```go -func MyPrompt(ctx context.Context, args mcp.GetPromptParams) (*mcp.GetPromptResult, error) { - return &mcp.GetPromptResult{ - Messages: []*mcp.PromptMessage{ - {Role: "user", Content: &mcp.TextContent{Text: "Prompt text"}}, - }, - }, nil -} -``` - -Register in `internal/mcp/server_sdk.go`. - -### 3. Add a Resource - -Create `internal/resources/myresource.go`: -```go -func MyResource(ctx context.Context, params mcp.ReadResourceParams) (*mcp.ReadResourceResult, error) { - return &mcp.ReadResourceResult{ - Contents: []*mcp.ResourceContents{{ - URI: "my://resource", - Text: "Content here", - }}, - }, nil -} -``` - -Register in `internal/mcp/server_sdk.go`. - -## 🔐 Authentication - -Enable OAuth: -```bash -export ENABLE_AUTH=true -export POSTGRES_HOST=localhost -export POSTGRES_DB=mcp_db -``` - -Disable for development: -```bash -export ENABLE_AUTH=false -``` +## Configuration -## 🚢 Deployment +| Variable | Default | Description | +|----------|---------|-------------| +| `MCP_TRANSPORT_PROTOCOL` | `http` | Transport: `stdio`, `http`, `sse` | +| `MCP_PORT` | `8081` | Server port | +| `CURSOR_COMPATIBLE_SSE` | `true` | Enable Cursor compatibility | +| `ENABLE_AUTH` | `true` | Enable OAuth authentication | +| `LOG_LEVEL` | `INFO` | Log level | -### Production Build -```bash -make build-prod -# Binary: bin/gomcp -``` +See [.env.example](.env.example) for all options. -### Docker Deployment -```bash -docker build -t gomcp-server . -docker run -p 8081:8081 --env-file .env gomcp-server -``` +## Testing -### Environment Setup ```bash -# Set transport -export MCP_TRANSPORT_PROTOCOL=http # or stdio -export MCP_PORT=8081 -export CURSOR_COMPATIBLE_SSE=true # For Cursor -export ENABLE_AUTH=false # For development -``` - -## 📊 Monitoring +# Run all tests +make test -### Metrics Endpoint -```bash -curl http://localhost:8081/metrics -``` +# Run with coverage +go test -coverprofile=coverage.out ./... +go tool cover -html=coverage.out -Returns: -- Request counts -- Tool usage -- Error rates -- Response times -- Client info +# Run linter +make lint -### Health Check -```bash -curl http://localhost:8081/health +# Run security scan +make security-scan ``` -## 🐛 Troubleshooting +## Documentation -**Port already in use:** -```bash -lsof -ti:8081 | xargs kill -9 -``` +| Document | Description | +|----------|-------------| +| [Architecture](docs/ARCHITECTURE.md) | System design and component overview | +| [Customization](docs/CUSTOMIZATION.md) | Guide to customizing the template | +| [Contributing](CONTRIBUTING.md) | How to contribute | +| [Security](SECURITY.md) | Security policy | -**Cursor not connecting:** -1. Use `make cursor` (Cursor compatibility is enabled by default) -2. Verify `~/.cursor/mcp.json` has correct URL: `http://localhost:8081/mcp/sse` -3. Restart Cursor IDE +### External Resources -**Claude Desktop not working:** -1. Set `MCP_TRANSPORT_PROTOCOL=stdio` -2. Use absolute path to binary -3. Restart Claude Desktop +- [MCP Specification](https://modelcontextprotocol.io/specification/2025-06-18) +- [Official Go SDK](https://github.com/modelcontextprotocol/go-sdk) -**Build errors:** -```bash -go mod tidy -go mod download -make clean && make build -``` +## Contributing -## 📚 Resources +We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. -- [MCP Specification](https://modelcontextprotocol.io/specification/2025-06-18) -- [Official Go SDK](https://github.com/modelcontextprotocol/go-sdk) +1. Fork the repository +2. Create a feature branch (`git checkout -b feature/amazing-feature`) +3. Commit your changes (`git commit -m 'feat: add amazing feature'`) +4. Push to the branch (`git push origin feature/amazing-feature`) +5. Open a Pull Request -## 📝 License +## License -MIT License - see LICENSE file +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. -## 🤝 Contributing +## Acknowledgments -1. Fork the repository -2. Create feature branch -3. Add tests -4. Submit pull request +- [Model Context Protocol](https://modelcontextprotocol.io/) - The MCP specification +- [Official Go SDK](https://github.com/modelcontextprotocol/go-sdk) - The foundation of this server +- All [contributors](https://github.com/NP-compete/gomcp/graphs/contributors) who help improve this project --- -**Built with ❤️ using Go and the official MCP SDK** +

+ Built for the MCP community +

-Template ready for production use with all MCP 2025-06-18 features! +

+ Star us on GitHub | + Report Bug | + Discussions +

diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..5cb0b32 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,118 @@ +# Security Policy + +## Supported Versions + +We release patches for security vulnerabilities for the following versions: + +| Version | Supported | +| ------- | ------------------ | +| 0.1.x | :white_check_mark: | + +## Reporting a Vulnerability + +We take the security of gomcp seriously. If you believe you have found a security vulnerability, please report it to us as described below. + +### Please Do + +- **Report the vulnerability privately** - Do not create a public GitHub issue for security vulnerabilities +- **Provide details** - Include as much information as possible about the vulnerability +- **Allow time** - Give us reasonable time to address the issue before any public disclosure + +### Please Don't + +- **Don't disclose publicly** - Please don't disclose the vulnerability publicly until we've had a chance to address it +- **Don't exploit** - Don't exploit the vulnerability beyond what's necessary to demonstrate it + +### How to Report + +1. **GitHub Security Advisories** (Preferred): Use [GitHub's security advisory feature](https://github.com/NP-compete/gomcp/security/advisories/new) to report vulnerabilities privately. + +2. **Email**: If you prefer email, contact the maintainers directly (check the repository for contact information). + +### What to Include + +When reporting a vulnerability, please include: + +- Type of vulnerability (e.g., injection, authentication bypass, etc.) +- Full paths of source file(s) related to the vulnerability +- Location of the affected source code (tag/branch/commit or direct URL) +- Step-by-step instructions to reproduce the issue +- Proof-of-concept or exploit code (if possible) +- Impact of the vulnerability and how an attacker might exploit it + +### Response Timeline + +- **Initial Response**: Within 48 hours, we will acknowledge receipt of your report +- **Status Update**: Within 7 days, we will provide an initial assessment +- **Resolution**: We aim to resolve critical vulnerabilities within 30 days + +### After Reporting + +1. We will confirm receipt of your vulnerability report +2. We will investigate and determine the impact +3. We will develop and test a fix +4. We will release a patch and publicly disclose the vulnerability (with credit to you, if desired) + +## Security Best Practices for Users + +When deploying gomcp in production: + +### Authentication + +- **Enable authentication** in production environments (`ENABLE_AUTH=true`) +- Use strong, unique session secrets (`SESSION_SECRET`) +- Configure OAuth/SSO with trusted identity providers + +### Network Security + +- Use HTTPS/TLS in production (configure `MCP_SSL_KEYFILE` and `MCP_SSL_CERTFILE`) +- Restrict CORS origins to trusted domains +- Use a reverse proxy (nginx, Caddy) for additional security headers + +### Environment Variables + +- Never commit `.env` files with secrets +- Use secret management solutions (Vault, AWS Secrets Manager, etc.) +- Rotate secrets regularly + +### Container Security + +- Run containers as non-root user (already configured in Dockerfile) +- Use read-only file systems where possible +- Scan images for vulnerabilities regularly + +### Monitoring + +- Enable logging and monitor for suspicious activity +- Set up alerts for authentication failures +- Review access logs regularly + +## Known Security Considerations + +### MCP Protocol + +- The MCP protocol allows AI models to execute tools - ensure tools are properly sandboxed +- Validate all inputs from MCP clients +- Be cautious with tools that access the filesystem or execute commands + +### Transport Security + +- **stdio transport**: Only use for local, trusted clients (Claude Desktop) +- **HTTP/SSE transport**: Always use HTTPS in production +- Validate client certificates when possible + +## Security Updates + +Security updates will be released as patch versions (e.g., 0.1.1, 0.1.2). We recommend: + +- Subscribing to GitHub releases for notifications +- Regularly updating to the latest patch version +- Reviewing the CHANGELOG for security-related fixes + +## Acknowledgments + +We appreciate the security research community's efforts in helping keep gomcp secure. Reporters of valid security issues will be acknowledged in our release notes (unless they prefer to remain anonymous). + +--- + +Thank you for helping keep gomcp and its users safe! diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..b34da01 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,359 @@ +# Architecture + +This document describes the architecture of gomcp, a production-ready MCP (Model Context Protocol) server template in Go. + +## Overview + +gomcp implements the [MCP 2025-06-18 specification](https://modelcontextprotocol.io/specification/2025-06-18), providing a complete server implementation that can be used as a template for building custom MCP servers. + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ MCP Clients │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ +│ │ Cursor IDE │ │ Claude │ │ HTTP/SSE │ │ Custom Clients │ │ +│ │ (HTTP/SSE) │ │ Desktop │ │ Clients │ │ │ │ +│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────────┬──────────┘ │ +└─────────┼────────────────┼────────────────┼────────────────────┼────────────┘ + │ │ │ │ + ▼ ▼ ▼ ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ Transport Layer │ +│ ┌─────────────────────────────┐ ┌─────────────────────────────────────┐ │ +│ │ HTTP/SSE Transport │ │ stdio Transport │ │ +│ │ (Cursor, Web Clients) │ │ (Claude Desktop, CLI) │ │ +│ └──────────────┬──────────────┘ └─────────────────┬───────────────────┘ │ +└─────────────────┼───────────────────────────────────┼───────────────────────┘ + │ │ + ▼ ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ MCP Server Core │ +│ ┌─────────────────────────────────────────────────────────────────────┐ │ +│ │ Official MCP Go SDK │ │ +│ │ (github.com/modelcontextprotocol/go-sdk) │ │ +│ └─────────────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ┌──────────────────────────┼──────────────────────────┐ │ +│ ▼ ▼ ▼ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ Tools │ │ Prompts │ │ Resources │ │ +│ │ (4 impl) │ │ (3 impl) │ │ (6 impl) │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────┐ │ +│ │ MCP Features │ │ +│ │ ┌────────┐ ┌────────┐ ┌──────────┐ ┌────────┐ ┌──────────────────┐ │ │ +│ │ │ Roots │ │Logging │ │Pagination│ │Sampling│ │ Elicitation │ │ │ +│ │ └────────┘ └────────┘ └──────────┘ └────────┘ └──────────────────┘ │ │ +│ │ ┌────────┐ ┌────────┐ ┌──────────┐ ┌────────┐ │ │ +│ │ │Progress│ │ Cancel │ │Completion│ │ Ping │ │ │ +│ │ └────────┘ └────────┘ └──────────┘ └────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +## Directory Structure + +``` +gomcp/ +├── cmd/ +│ └── server/ +│ └── main.go # Application entry point +├── internal/ +│ ├── api/ +│ │ ├── router.go # HTTP router setup +│ │ ├── handlers.go # HTTP request handlers +│ │ ├── metrics.go # Metrics endpoint +│ │ └── mcp_metrics.go # MCP-specific metrics +│ ├── completion/ +│ │ ├── completion.go # Structured output completion +│ │ └── completion_test.go # Tests +│ ├── config/ +│ │ └── config.go # Configuration management +│ ├── elicitation/ +│ │ └── elicitation.go # User data elicitation +│ ├── errors/ +│ │ └── errors.go # Error types and handling +│ ├── logger/ +│ │ └── logger.go # Application logging +│ ├── logging/ +│ │ ├── logging.go # MCP logging (server-to-client) +│ │ └── logging_test.go # Tests +│ ├── mcp/ +│ │ ├── server.go # MCP server (HTTP transport) +│ │ └── server_sdk.go # MCP server (SDK integration) +│ ├── middleware/ +│ │ ├── auth.go # Authentication middleware +│ │ ├── cors.go # CORS middleware +│ │ ├── logging.go # Request logging +│ │ ├── recovery.go # Panic recovery +│ │ ├── requestid.go # Request ID generation +│ │ └── session.go # Session management +│ ├── oauth/ +│ │ └── oauth.go # OAuth service +│ ├── pagination/ +│ │ ├── pagination.go # Cursor-based pagination +│ │ └── pagination_test.go # Tests +│ ├── prompts/ +│ │ ├── code_review.go # Code review prompt +│ │ ├── debug_help.go # Debug help prompt +│ │ └── git_commit.go # Git commit prompt +│ ├── resources/ +│ │ ├── config.go # Config resources +│ │ ├── docs.go # Documentation resources +│ │ └── project_info.go # Project info resources +│ ├── roots/ +│ │ ├── roots.go # Filesystem roots +│ │ └── roots_test.go # Tests +│ ├── sampling/ +│ │ └── sampling.go # LLM sampling requests +│ ├── storage/ +│ │ ├── interface.go # Storage interface +│ │ ├── memory.go # In-memory storage +│ │ └── storage.go # Storage implementation +│ ├── tools/ +│ │ ├── codereview.go # Code review tool +│ │ ├── codereview_sdk.go # SDK integration +│ │ ├── logo.go # Logo tool +│ │ ├── logo_sdk.go # SDK integration +│ │ ├── long_operation_sdk.go# Long operation tool +│ │ ├── multiply.go # Multiply tool +│ │ └── multiply_sdk.go # SDK integration +│ └── version/ +│ └── version.go # Version information +├── pkg/ +│ └── mcpprotocol/ +│ ├── handler.go # Protocol handler +│ └── types.go # Protocol types +├── scripts/ +│ ├── health-check.sh # Health check script +│ ├── init-db.sql # Database initialization +│ ├── run_dev.sh # Development runner +│ ├── start.sh # Start script +│ ├── stop.sh # Stop script +│ └── test_all.sh # Test runner +├── test/ +│ └── integration_test.go # Integration tests +└── docs/ + ├── ARCHITECTURE.md # This file + └── CUSTOMIZATION.md # Template customization guide +``` + +## Core Components + +### 1. Transport Layer + +gomcp supports two transport mechanisms: + +#### HTTP/SSE Transport +- Used by Cursor IDE and web-based clients +- Server-Sent Events (SSE) for real-time communication +- Cursor compatibility mode for IDE integration +- CORS support for browser clients + +#### stdio Transport +- Used by Claude Desktop and CLI tools +- Standard input/output communication +- No network configuration required + +### 2. MCP Server Core + +The server core is built on the [official MCP Go SDK](https://github.com/modelcontextprotocol/go-sdk): + +```go +// Server initialization +impl := &mcp.Implementation{ + Name: "template", + Version: "0.1.0", +} +server := mcp.NewServer(impl, nil) +``` + +### 3. Tools + +Tools are functions that AI models can invoke: + +| Tool | Description | Type | +|------|-------------|------| +| `multiply_numbers` | Multiply two numbers | CPU-bound | +| `code_review` | Generate code review | I/O-bound | +| `logo` | Return server logo | Resource-as-tool | +| `long_operation` | Demo progress/cancel | Long-running | + +**Tool Implementation Pattern:** + +```go +type ToolInput struct { + Param string `json:"param" jsonschema:"required,description=..."` +} + +type ToolOutput struct { + Result string `json:"result"` +} + +func ToolSDK(ctx context.Context, req *mcp.CallToolRequest, input ToolInput) (*mcp.CallToolResult, ToolOutput, error) { + // Implementation + return nil, output, nil +} +``` + +### 4. Prompts + +Prompts are reusable templates for AI interactions: + +| Prompt | Description | +|--------|-------------| +| `code_review` | Structured code review template | +| `git_commit` | Conventional commit message generator | +| `debug_help` | Debugging assistance template | + +### 5. Resources + +Resources provide data to AI models: + +| Resource | URI | Type | +|----------|-----|------| +| Project Info | `project://info` | Dynamic | +| System Status | `system://status` | Dynamic | +| Quick Start | `docs://quickstart` | Static | +| API Reference | `docs://api` | Static | +| Config Template | `config://template` | Static | +| Env Vars | `config://env-vars` | Static | + +### 6. MCP Features + +| Feature | Description | Implementation | +|---------|-------------|----------------| +| **Roots** | Filesystem root definitions | `internal/roots/` | +| **Completion** | Structured tool outputs | `internal/completion/` | +| **Logging** | Server-to-client notifications | `internal/logging/` | +| **Pagination** | Cursor-based pagination | `internal/pagination/` | +| **Sampling** | Server-to-client LLM requests | `internal/sampling/` | +| **Elicitation** | Server-to-user data requests | `internal/elicitation/` | +| **Progress** | Real-time progress updates | Built into tools | +| **Cancellation** | Request cancellation | Context-based | +| **Ping** | Health checks | Built-in | + +## Data Flow + +### HTTP/SSE Request Flow + +``` +Client Request + │ + ▼ +┌─────────────┐ +│ Router │ ─── Middleware Chain ───┐ +└─────────────┘ │ + │ │ + ▼ ▼ +┌─────────────┐ ┌─────────────┐ +│ Handlers │ │ Logging │ +└─────────────┘ │ CORS │ + │ │ Auth │ + ▼ │ Recovery │ +┌─────────────┐ └─────────────┘ +│ MCP Server │ +└─────────────┘ + │ + ├──────────┬──────────┬──────────┐ + ▼ ▼ ▼ ▼ + Tools Prompts Resources Features +``` + +### stdio Request Flow + +``` +stdin ──► MCP SDK Server ──► Tools/Prompts/Resources ──► stdout +``` + +## Configuration + +Configuration is managed through environment variables: + +```go +type Config struct { + // Server + MCPHost string + MCPPort int + MCPTransportProtocol string // "http", "sse", "stdio" + + // Security + EnableAuth bool + SessionSecret string + + // Cursor Compatibility + CursorCompatibleSSE bool + + // SSL/TLS + MCPSSLKeyfile string + MCPSSLCertfile string +} +``` + +## Extension Points + +### Adding a New Tool + +1. Create `internal/tools/newtool_sdk.go` +2. Define input/output structs with JSON schema tags +3. Implement the tool function +4. Register in `internal/mcp/server_sdk.go` +5. Add tests + +### Adding a New Prompt + +1. Create `internal/prompts/newprompt.go` +2. Define argument struct +3. Implement the prompt function +4. Register in `internal/mcp/server_sdk.go` +5. Add tests + +### Adding a New Resource + +1. Create `internal/resources/newresource.go` +2. Implement the resource function +3. Register in `internal/mcp/server_sdk.go` +4. Add tests + +## Security Considerations + +1. **Authentication**: OAuth support with session management +2. **Transport Security**: SSL/TLS support for HTTP transport +3. **Input Validation**: All inputs validated before processing +4. **Error Handling**: Sensitive information not exposed in errors +5. **Container Security**: Non-root user in Docker + +## Performance + +- **Connection Pooling**: Database connections pooled +- **Caching**: Resource caching where appropriate +- **Pagination**: Cursor-based pagination for large datasets +- **Timeouts**: Configurable request timeouts + +## Testing + +```bash +# Unit tests +go test ./... + +# With coverage +go test -coverprofile=coverage.out ./... + +# Integration tests +go test -v ./test/... + +# Benchmarks +go test -bench=. ./... +``` + +## Monitoring + +- **Health Check**: `GET /health` +- **Metrics**: `GET /metrics` +- **Logging**: Structured JSON logging with zerolog + +## Related Documentation + +- [MCP Specification](https://modelcontextprotocol.io/specification/2025-06-18) +- [Official Go SDK](https://github.com/modelcontextprotocol/go-sdk) +- [Customization Guide](CUSTOMIZATION.md) diff --git a/docs/CUSTOMIZATION.md b/docs/CUSTOMIZATION.md new file mode 100644 index 0000000..b0e2075 --- /dev/null +++ b/docs/CUSTOMIZATION.md @@ -0,0 +1,573 @@ +# Template Customization Guide + +This guide walks you through customizing the gomcp template for your own MCP server. + +## Quick Start + +### 1. Use This Template + +Click **"Use this template"** on GitHub to create your own repository, or: + +```bash +# Clone the template +git clone https://github.com/NP-compete/gomcp.git my-mcp-server +cd my-mcp-server + +# Remove git history and start fresh +rm -rf .git +git init +git add . +git commit -m "Initial commit from gomcp template" +``` + +### 2. Rename the Module + +Update the Go module name in `go.mod`: + +```go +// Before +module github.com/NP-compete/gomcp + +// After +module github.com/YOUR_USERNAME/my-mcp-server +``` + +Then update all imports: + +```bash +# Find and replace all import paths +find . -type f -name "*.go" -exec sed -i '' 's|github.com/NP-compete/gomcp|github.com/YOUR_USERNAME/my-mcp-server|g' {} + + +# Update Makefile LDFLAGS +sed -i '' 's|github.com/NP-compete/gomcp|github.com/YOUR_USERNAME/my-mcp-server|g' Makefile +``` + +### 3. Update Server Identity + +Edit `internal/mcp/server_sdk.go`: + +```go +impl := &mcp.Implementation{ + Name: "my-server", // Your server name + Version: "1.0.0", // Your version +} +``` + +### 4. Customize Configuration + +Update `.env.example` and `internal/config/config.go` with your settings. + +## Adding Your Own Tools + +### Step 1: Create the Tool File + +Create `internal/tools/weather_sdk.go`: + +```go +package tools + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/modelcontextprotocol/go-sdk/mcp" +) + +// WeatherInput defines the tool's input parameters +type WeatherInput struct { + City string `json:"city" jsonschema:"required,description=City name to get weather for"` + Units string `json:"units" jsonschema:"enum=celsius,enum=fahrenheit,default=celsius,description=Temperature units"` +} + +// WeatherOutput defines the tool's output structure +type WeatherOutput struct { + City string `json:"city"` + Temperature float64 `json:"temperature"` + Units string `json:"units"` + Condition string `json:"condition"` + Humidity int `json:"humidity"` +} + +// GetWeatherSDK implements the weather lookup tool +func GetWeatherSDK(ctx context.Context, req *mcp.CallToolRequest, input WeatherInput) (*mcp.CallToolResult, WeatherOutput, error) { + // Set default units + if input.Units == "" { + input.Units = "celsius" + } + + // Your weather API logic here + // This is a placeholder - replace with actual API call + output := WeatherOutput{ + City: input.City, + Temperature: 22.5, + Units: input.Units, + Condition: "Sunny", + Humidity: 65, + } + + return nil, output, nil +} +``` + +### Step 2: Register the Tool + +Edit `internal/mcp/server_sdk.go`: + +```go +func registerToolsSDK(server *mcp.Server) { + // ... existing tools ... + + // Register weather tool + mcp.AddTool( + server, + &mcp.Tool{ + Name: "get_weather", + Description: "Get current weather for a city. Returns temperature, conditions, and humidity.", + }, + tools.GetWeatherSDK, + ) +} +``` + +### Step 3: Add Tests + +Create `internal/tools/weather_test.go`: + +```go +package tools + +import ( + "context" + "testing" + + "github.com/modelcontextprotocol/go-sdk/mcp" +) + +func TestGetWeatherSDK(t *testing.T) { + tests := []struct { + name string + input WeatherInput + wantErr bool + }{ + { + name: "valid city", + input: WeatherInput{City: "London", Units: "celsius"}, + wantErr: false, + }, + { + name: "default units", + input: WeatherInput{City: "Paris"}, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + req := &mcp.CallToolRequest{} + + _, output, err := GetWeatherSDK(ctx, req, tt.input) + + if (err != nil) != tt.wantErr { + t.Errorf("GetWeatherSDK() error = %v, wantErr %v", err, tt.wantErr) + } + + if output.City != tt.input.City { + t.Errorf("GetWeatherSDK() city = %v, want %v", output.City, tt.input.City) + } + }) + } +} +``` + +## Adding Your Own Prompts + +### Step 1: Create the Prompt File + +Create `internal/prompts/summarize.go`: + +```go +package prompts + +import ( + "context" + "fmt" + + "github.com/modelcontextprotocol/go-sdk/mcp" +) + +// SummarizePromptArgs defines the prompt arguments +type SummarizePromptArgs struct { + Text string `json:"text"` + Style string `json:"style"` // "brief", "detailed", "bullet" + MaxLen int `json:"max_length"` +} + +// GetSummarizePrompt generates a text summarization prompt +func GetSummarizePrompt(ctx context.Context, req *mcp.GetPromptRequest, args SummarizePromptArgs) (*mcp.GetPromptResult, error) { + // Set defaults + if args.Style == "" { + args.Style = "brief" + } + if args.MaxLen == 0 { + args.MaxLen = 200 + } + + styleInstructions := map[string]string{ + "brief": "Provide a concise summary in 1-2 sentences.", + "detailed": "Provide a comprehensive summary covering all key points.", + "bullet": "Provide a summary as bullet points.", + } + + promptText := fmt.Sprintf(`Summarize the following text. + +Style: %s +Maximum Length: %d words + +Instructions: %s + +Text to summarize: +--- +%s +--- + +Summary:`, + args.Style, + args.MaxLen, + styleInstructions[args.Style], + args.Text, + ) + + return &mcp.GetPromptResult{ + Messages: []*mcp.PromptMessage{ + { + Role: "user", + Content: &mcp.TextContent{ + Type: "text", + Text: promptText, + }, + }, + }, + }, nil +} +``` + +### Step 2: Register the Prompt + +Edit `internal/mcp/server_sdk.go`: + +```go +func registerPromptsSDK(server *mcp.Server) { + // ... existing prompts ... + + // Register summarize prompt + server.AddPrompt( + &mcp.Prompt{ + Name: "summarize", + Description: "Generate a text summarization prompt with configurable style", + }, + func(ctx context.Context, req *mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { + var args prompts.SummarizePromptArgs + if req.Params.Arguments != nil { + if text, ok := req.Params.Arguments["text"]; ok { + args.Text = text + } + if style, ok := req.Params.Arguments["style"]; ok { + args.Style = style + } + // Parse max_length as needed + } + return prompts.GetSummarizePrompt(ctx, req, args) + }, + ) +} +``` + +## Adding Your Own Resources + +### Step 1: Create the Resource File + +Create `internal/resources/database.go`: + +```go +package resources + +import ( + "context" + "encoding/json" + + "github.com/modelcontextprotocol/go-sdk/mcp" +) + +// DatabaseSchema represents the database schema resource +type DatabaseSchema struct { + Tables []TableInfo `json:"tables"` +} + +type TableInfo struct { + Name string `json:"name"` + Columns []ColumnInfo `json:"columns"` +} + +type ColumnInfo struct { + Name string `json:"name"` + Type string `json:"type"` + Nullable bool `json:"nullable"` +} + +// GetDatabaseSchema returns the database schema as a resource +func GetDatabaseSchema(ctx context.Context, req *mcp.ReadResourceRequest) (*mcp.ReadResourceResult, error) { + // In a real implementation, query your database for schema info + schema := DatabaseSchema{ + Tables: []TableInfo{ + { + Name: "users", + Columns: []ColumnInfo{ + {Name: "id", Type: "uuid", Nullable: false}, + {Name: "email", Type: "varchar", Nullable: false}, + {Name: "created_at", Type: "timestamp", Nullable: false}, + }, + }, + // Add more tables... + }, + } + + data, err := json.MarshalIndent(schema, "", " ") + if err != nil { + return nil, err + } + + return &mcp.ReadResourceResult{ + Contents: []*mcp.ResourceContents{ + { + URI: "database://schema", + MIMEType: "application/json", + Text: string(data), + }, + }, + }, nil +} +``` + +### Step 2: Register the Resource + +Edit `internal/mcp/server_sdk.go`: + +```go +func registerResourcesSDK(server *mcp.Server) { + // ... existing resources ... + + // Register database schema resource + server.AddResource( + &mcp.Resource{ + URI: "database://schema", + Name: "Database Schema", + Description: "Current database schema with all tables and columns", + MIMEType: "application/json", + }, + resources.GetDatabaseSchema, + ) +} +``` + +## Removing Example Code + +To start with a clean slate, remove the example implementations: + +```bash +# Remove example tools (keep the files as templates) +rm internal/tools/multiply*.go +rm internal/tools/codereview*.go +rm internal/tools/logo*.go +rm internal/tools/long_operation*.go + +# Remove example prompts +rm internal/prompts/code_review.go +rm internal/prompts/debug_help.go +rm internal/prompts/git_commit.go + +# Remove example resources +rm internal/resources/config.go +rm internal/resources/docs.go +rm internal/resources/project_info.go +``` + +Then update `internal/mcp/server_sdk.go` to remove the registrations. + +## Configuration Customization + +### Adding New Environment Variables + +1. Add to `internal/config/config.go`: + +```go +type Config struct { + // ... existing fields ... + + // Your custom config + MyAPIKey string `mapstructure:"MY_API_KEY"` + MyAPIBaseURL string `mapstructure:"MY_API_BASE_URL"` +} +``` + +2. Add to `.env.example`: + +```env +# My API Configuration +MY_API_KEY=your-api-key-here +MY_API_BASE_URL=https://api.example.com +``` + +3. Add validation in `config.Validate()` if needed. + +## Docker Customization + +### Adding Dependencies + +Edit `Dockerfile` to add system dependencies: + +```dockerfile +# Build stage +FROM golang:1.23-alpine AS builder + +# Add your build dependencies +RUN apk add --no-cache git ca-certificates gcc musl-dev + +# ... rest of build ... + +# Final stage +FROM alpine:latest + +# Add your runtime dependencies +RUN apk --no-cache add ca-certificates tzdata + +# ... rest of runtime ... +``` + +### Multi-stage Builds for Additional Assets + +```dockerfile +# Build stage +FROM golang:1.23-alpine AS builder +# ... build Go binary ... + +# Asset stage (if you need to process assets) +FROM node:20-alpine AS assets +WORKDIR /assets +COPY frontend/ . +RUN npm ci && npm run build + +# Final stage +FROM alpine:latest +COPY --from=builder /app/bin/gomcp /app/gomcp +COPY --from=assets /assets/dist /app/static +``` + +## CI/CD Customization + +### Adding Custom Checks + +Edit `.github/workflows/ci.yml`: + +```yaml +jobs: + # ... existing jobs ... + + custom-check: + name: Custom Validation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Run custom validation + run: | + # Your custom checks here + ./scripts/validate.sh +``` + +### Adding Deployment + +Add to `.github/workflows/release.yml`: + +```yaml + deploy: + name: Deploy to Production + runs-on: ubuntu-latest + needs: [build, docker] + if: github.ref == 'refs/heads/main' + steps: + - name: Deploy + run: | + # Your deployment logic +``` + +## Testing Your Customizations + +### Local Testing + +```bash +# Run all tests +make test + +# Test specific package +go test -v ./internal/tools/... + +# Run with coverage +go test -coverprofile=coverage.out ./... +go tool cover -html=coverage.out +``` + +### Integration Testing + +```bash +# Start server +make run + +# In another terminal, test with curl +curl http://localhost:8081/health + +# Test MCP endpoint +curl -X POST http://localhost:8081/mcp \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' +``` + +### Testing with Cursor IDE + +1. Build the server: `make cursor` +2. Configure `~/.cursor/mcp.json`: + ```json + { + "mcpServers": { + "my-server": { + "url": "http://localhost:8081/mcp/sse" + } + } + } + ``` +3. Restart Cursor IDE +4. Test your tools in the chat + +## Checklist + +Before publishing your customized server: + +- [ ] Updated module name in `go.mod` +- [ ] Updated all import paths +- [ ] Changed server name and version +- [ ] Added your own tools/prompts/resources +- [ ] Removed or modified example code +- [ ] Updated README.md with your documentation +- [ ] Updated CHANGELOG.md +- [ ] Configured CI/CD for your repository +- [ ] Added appropriate LICENSE +- [ ] Tested with target clients (Cursor, Claude Desktop) + +## Getting Help + +- [MCP Specification](https://modelcontextprotocol.io/specification/2025-06-18) +- [Official Go SDK Documentation](https://github.com/modelcontextprotocol/go-sdk) +- [gomcp Issues](https://github.com/NP-compete/gomcp/issues) +- [gomcp Discussions](https://github.com/NP-compete/gomcp/discussions) diff --git a/internal/api/mcp_metrics.go b/internal/api/mcp_metrics.go index c74ead1..ce69a93 100644 --- a/internal/api/mcp_metrics.go +++ b/internal/api/mcp_metrics.go @@ -27,17 +27,17 @@ type mcpMetricsInternal struct { // EndpointMetrics tracks metrics for a specific endpoint type EndpointMetrics struct { - TotalRequests int64 `json:"total_requests"` - ToolCalls int64 `json:"tool_calls"` - ToolsListCalls int64 `json:"tools_list_calls"` - InitializeCalls int64 `json:"initialize_calls"` - Errors int64 `json:"errors"` - LastRequestTime time.Time `json:"last_request_time"` - FirstRequestTime time.Time `json:"first_request_time"` - ClientUserAgents map[string]int64 `json:"client_user_agents"` // User-Agent -> count - ClientIPs map[string]int64 `json:"client_ips"` // IP -> count - AvgResponseTime float64 `json:"avg_response_time_ms"` - TotalResponseTime float64 `json:"total_response_time_ms"` + TotalRequests int64 `json:"total_requests"` + ToolCalls int64 `json:"tool_calls"` + ToolsListCalls int64 `json:"tools_list_calls"` + InitializeCalls int64 `json:"initialize_calls"` + Errors int64 `json:"errors"` + LastRequestTime time.Time `json:"last_request_time"` + FirstRequestTime time.Time `json:"first_request_time"` + ClientUserAgents map[string]int64 `json:"client_user_agents"` // User-Agent -> count + ClientIPs map[string]int64 `json:"client_ips"` // IP -> count + AvgResponseTime float64 `json:"avg_response_time_ms"` + TotalResponseTime float64 `json:"total_response_time_ms"` } // ToolMetrics tracks per-tool usage across both endpoints @@ -255,4 +255,3 @@ func ResetMCPMetrics() { mcpMetrics.metrics.ToolCalls = make(map[string]*ToolMetrics) mcpMetrics.metrics.LastReset = time.Now() } - diff --git a/internal/api/router.go b/internal/api/router.go index c26d305..b7326e1 100644 --- a/internal/api/router.go +++ b/internal/api/router.go @@ -4,14 +4,14 @@ import ( "net/http" "time" - "github.com/go-chi/chi/v5" - chimiddleware "github.com/go-chi/chi/v5/middleware" "github.com/NP-compete/gomcp/internal/config" "github.com/NP-compete/gomcp/internal/logger" "github.com/NP-compete/gomcp/internal/mcp" "github.com/NP-compete/gomcp/internal/middleware" "github.com/NP-compete/gomcp/internal/oauth" "github.com/NP-compete/gomcp/internal/storage" + "github.com/go-chi/chi/v5" + chimiddleware "github.com/go-chi/chi/v5/middleware" ) // StorageService is an alias for storage.Service (PostgreSQL) diff --git a/internal/elicitation/elicitation.go b/internal/elicitation/elicitation.go index 62a1d66..9c06f5c 100644 --- a/internal/elicitation/elicitation.go +++ b/internal/elicitation/elicitation.go @@ -4,8 +4,8 @@ import ( "context" "fmt" - mcp "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/NP-compete/gomcp/internal/logger" + mcp "github.com/modelcontextprotocol/go-sdk/mcp" ) // ResponseAction represents the user's response to an elicitation request diff --git a/internal/logging/logging.go b/internal/logging/logging.go index d513297..a6f71c1 100644 --- a/internal/logging/logging.go +++ b/internal/logging/logging.go @@ -5,8 +5,8 @@ import ( "fmt" "time" - mcp "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/NP-compete/gomcp/internal/logger" + mcp "github.com/modelcontextprotocol/go-sdk/mcp" ) // LogLevel represents the severity of a log message @@ -41,16 +41,10 @@ func LogToClient(ctx context.Context, session *mcp.ServerSession, level LogLevel return nil } - _ = LogMessage{ - Level: level, - Logger: "gomcp-server", - Data: data, - Timestamp: time.Now().UTC(), - } - // Note: The official SDK may not have direct logging notification support yet // This is a placeholder for the proper implementation // In production, this would call: session.SendNotification(ctx, "notifications/message", logMsg) + // with a LogMessage struct containing level, logger, data, and timestamp // For now, log to server logs as fallback logger.Info(fmt.Sprintf("[CLIENT LOG %s] %s: %v", level, message, data)) diff --git a/internal/mcp/server_sdk.go b/internal/mcp/server_sdk.go index bbd4b27..8dfd78d 100644 --- a/internal/mcp/server_sdk.go +++ b/internal/mcp/server_sdk.go @@ -3,11 +3,11 @@ package mcp import ( "context" - "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/NP-compete/gomcp/internal/logger" "github.com/NP-compete/gomcp/internal/prompts" "github.com/NP-compete/gomcp/internal/resources" "github.com/NP-compete/gomcp/internal/tools" + "github.com/modelcontextprotocol/go-sdk/mcp" ) // ServerSDK wraps the official MCP SDK server diff --git a/internal/resources/project_info.go b/internal/resources/project_info.go index 2484661..b6561de 100644 --- a/internal/resources/project_info.go +++ b/internal/resources/project_info.go @@ -6,8 +6,8 @@ import ( "fmt" "runtime" - "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/NP-compete/gomcp/internal/version" + "github.com/modelcontextprotocol/go-sdk/mcp" ) // GetProjectInfo returns information about the gomcp server diff --git a/internal/sampling/sampling.go b/internal/sampling/sampling.go index 6388a56..7d46349 100644 --- a/internal/sampling/sampling.go +++ b/internal/sampling/sampling.go @@ -4,8 +4,8 @@ import ( "context" "fmt" - mcp "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/NP-compete/gomcp/internal/logger" + mcp "github.com/modelcontextprotocol/go-sdk/mcp" ) // CreateMessageSampling handles sampling requests from the server to the client @@ -16,15 +16,14 @@ func CreateMessageSampling( messages []*mcp.SamplingMessage, modelPreferences *mcp.ModelPreferences, systemPrompt string, - maxTokens int, + maxTokens int64, ) (*mcp.CreateMessageResult, error) { if session == nil { return nil, fmt.Errorf("session is nil - cannot send sampling request") } - // Build sampling request - params := &mcp.CreateMessageRequestParams{ + params := &mcp.CreateMessageParams{ Messages: messages, MaxTokens: maxTokens, } @@ -39,34 +38,26 @@ func CreateMessageSampling( logger.Info(fmt.Sprintf("Sending sampling request to client (maxTokens=%d)", maxTokens)) - // Send sampling request to client - result, err := session.CreateMessage(ctx, &mcp.CreateMessageRequest{ - Params: *params, - }) + result, err := session.CreateMessage(ctx, params) if err != nil { logger.Error(fmt.Sprintf("Sampling request failed: %v", err)) return nil, err } - logger.Info(fmt.Sprintf("Sampling response received: %d messages", len(result.Content))) + logger.Info("Sampling response received") return result, nil } // ExampleSamplingUsage demonstrates how to use sampling in a tool func ExampleSamplingUsage(ctx context.Context, session *mcp.ServerSession, userQuery string) (string, error) { - // Build messages for the LLM messages := []*mcp.SamplingMessage{ { - Role: mcp.RoleUser, - Content: mcp.TextContent{ - Type: "text", - Text: userQuery, - }, + Role: mcp.Role("user"), + Content: &mcp.TextContent{Text: userQuery}, }, } - // Optional: Specify model preferences modelPrefs := &mcp.ModelPreferences{ Hints: []*mcp.ModelHint{ { @@ -78,23 +69,21 @@ func ExampleSamplingUsage(ctx context.Context, session *mcp.ServerSession, userQ IntelligencePriority: 0.8, } - // Request sampling result, err := CreateMessageSampling( ctx, session, messages, modelPrefs, "You are a helpful assistant.", - 1000, // max tokens + 1000, ) if err != nil { return "", err } - // Extract text from response - if len(result.Content) > 0 { - if textContent, ok := result.Content[0].(mcp.TextContent); ok { + if result.Content != nil { + if textContent, ok := result.Content.(*mcp.TextContent); ok { return textContent.Text, nil } } diff --git a/internal/storage/storage.go b/internal/storage/storage.go index d72ca5d..9a574ae 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -5,9 +5,9 @@ import ( "encoding/json" "fmt" + "github.com/NP-compete/gomcp/internal/logger" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" - "github.com/NP-compete/gomcp/internal/logger" ) // Service provides PostgreSQL storage functionality diff --git a/internal/tools/codereview_sdk.go b/internal/tools/codereview_sdk.go index ce5f4f3..4f3dcb5 100644 --- a/internal/tools/codereview_sdk.go +++ b/internal/tools/codereview_sdk.go @@ -4,8 +4,8 @@ import ( "context" "fmt" - "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/NP-compete/gomcp/internal/logger" + "github.com/modelcontextprotocol/go-sdk/mcp" ) // CodeReviewInput defines the input schema for generate_code_review_prompt tool diff --git a/internal/tools/logo_sdk.go b/internal/tools/logo_sdk.go index 6e188fe..ae50bd9 100644 --- a/internal/tools/logo_sdk.go +++ b/internal/tools/logo_sdk.go @@ -7,8 +7,8 @@ import ( "os" "path/filepath" - "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/NP-compete/gomcp/internal/logger" + "github.com/modelcontextprotocol/go-sdk/mcp" ) // LogoInput defines the input schema for get_redhat_logo tool (no parameters) diff --git a/internal/tools/long_operation_sdk.go b/internal/tools/long_operation_sdk.go index c97eec5..010bb61 100644 --- a/internal/tools/long_operation_sdk.go +++ b/internal/tools/long_operation_sdk.go @@ -5,8 +5,8 @@ import ( "fmt" "time" - "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/NP-compete/gomcp/internal/logger" + "github.com/modelcontextprotocol/go-sdk/mcp" ) // LongOperationInput defines the input schema for long_operation tool diff --git a/internal/tools/multiply_sdk.go b/internal/tools/multiply_sdk.go index 603f81e..3111198 100644 --- a/internal/tools/multiply_sdk.go +++ b/internal/tools/multiply_sdk.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/NP-compete/gomcp/internal/completion" "github.com/NP-compete/gomcp/internal/logger" + "github.com/modelcontextprotocol/go-sdk/mcp" ) // MultiplyInput defines the input schema for multiply_numbers tool diff --git a/scripts/run_dev.sh b/scripts/run_dev.sh index c0c2a84..16dfc13 100755 --- a/scripts/run_dev.sh +++ b/scripts/run_dev.sh @@ -5,24 +5,24 @@ set -e -echo "🔧 Setting up development environment..." +echo "Setting up development environment..." # Check if air is installed if ! command -v air &> /dev/null; then - echo "📦 Installing air..." + echo "Installing air..." go install github.com/air-verse/air@latest # Add GOPATH/bin to PATH if not already there GOBIN=$(go env GOPATH)/bin if [[ ":$PATH:" != *":$GOBIN:"* ]]; then export PATH=$PATH:$GOBIN - echo "✅ Added $GOBIN to PATH" + echo "Added $GOBIN to PATH" fi fi # Check if port 8081 is in use if lsof -Pi :8081 -sTCP:LISTEN -t >/dev/null 2>&1; then - echo "⚠️ Port 8081 is already in use. Stopping existing server..." + echo "Port 8081 is already in use. Stopping existing server..." lsof -ti:8081 | xargs kill -9 2>/dev/null || true sleep 1 fi @@ -31,13 +31,12 @@ fi rm -rf tmp/ mkdir -p tmp/ -echo "🚀 Starting development server with hot reload..." -echo "📍 Server will run on http://localhost:8081/mcp/sse" -echo "🔄 Files will auto-reload on changes" +echo "Starting development server with hot reload..." +echo "Server will run on http://localhost:8081/mcp/sse" +echo "Files will auto-reload on changes" echo "" echo "Press Ctrl+C to stop" echo "" # Run air with Cursor-compatible settings air - diff --git a/scripts/test_all.sh b/scripts/test_all.sh index 1caba4a..aa2e3c1 100755 --- a/scripts/test_all.sh +++ b/scripts/test_all.sh @@ -5,7 +5,7 @@ set -e -echo "🧪 MCP 2025-06-18 Test Suite" +echo "MCP 2025-06-18 Test Suite" echo "==============================" echo "" @@ -30,17 +30,17 @@ run_test() { echo -e "${YELLOW}Testing:${NC} $description" if go test -v "./$package" 2>&1 | tee /tmp/test_output.log; then - echo -e "${GREEN}✓ PASSED${NC}: $description" + echo -e "${GREEN}PASSED${NC}: $description" TESTS_PASSED=$((TESTS_PASSED + 1)) else - echo -e "${RED}✗ FAILED${NC}: $description" + echo -e "${RED}FAILED${NC}: $description" TESTS_FAILED=$((TESTS_FAILED + 1)) cat /tmp/test_output.log fi echo "" } -echo "📦 Unit Tests" +echo "Unit Tests" echo "-------------" echo "" @@ -48,7 +48,7 @@ echo "" run_test "internal/completion" "Completion (Structured Outputs)" # Logging tests -run_test "internal/logging" "Logging (Server→Client Notifications)" +run_test "internal/logging" "Logging (Server to Client Notifications)" # Pagination tests run_test "internal/pagination" "Pagination (Cursor-Based)" @@ -57,7 +57,7 @@ run_test "internal/pagination" "Pagination (Cursor-Based)" run_test "internal/roots" "Roots (Filesystem Boundaries)" echo "" -echo "🔗 Integration Tests" +echo "Integration Tests" echo "-------------------" echo "" @@ -65,7 +65,7 @@ echo "" run_test "test" "Full MCP Protocol Integration" echo "" -echo "📊 Test Summary" +echo "Test Summary" echo "===============" echo "" @@ -77,7 +77,7 @@ echo -e "${RED}Failed: $TESTS_FAILED${NC}" echo "" # Coverage report -echo "📈 Generating Coverage Report" +echo "Generating Coverage Report" echo "==============================" echo "" @@ -89,10 +89,9 @@ echo "" # Exit with error if any tests failed if [ $TESTS_FAILED -gt 0 ]; then - echo -e "${RED}❌ Some tests failed!${NC}" + echo -e "${RED}Some tests failed!${NC}" exit 1 else - echo -e "${GREEN}✅ All tests passed!${NC}" + echo -e "${GREEN}All tests passed!${NC}" exit 0 fi -