Compare commits
212 changed files with 4726 additions and 50491 deletions
|
|
@ -1,13 +0,0 @@
|
||||||
---
|
|
||||||
name: fix-bug
|
|
||||||
description: Confirm, debug and fix bugs reported via GitHub issues.
|
|
||||||
---
|
|
||||||
|
|
||||||
# Step-by-step instructions
|
|
||||||
|
|
||||||
1. Use the `gh` CLI to fetch information about the issue.
|
|
||||||
2. Create a reproduction test case.
|
|
||||||
3. Create an implementation plan that describes how you intend to fix the bug. Don't proceed without approval.
|
|
||||||
4. Implement the fix.
|
|
||||||
5. Verify the fix with the test case and run all tests to ensure no regressions.
|
|
||||||
6. Provide a summary of the changes.
|
|
||||||
25
.github/ISSUE_TEMPLATE/bug_report.md
vendored
25
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
|
@ -1,25 +0,0 @@
|
||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Create a report to help us improve
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
type: Bug
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Describe the bug**
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
**To Reproduce**
|
|
||||||
Steps to reproduce the behavior:
|
|
||||||
1.
|
|
||||||
|
|
||||||
**Expected behavior**
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**Logs**
|
|
||||||
If applicable, add logs to help explain your problem.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context about the problem here.
|
|
||||||
21
.github/ISSUE_TEMPLATE/enhancement.md
vendored
21
.github/ISSUE_TEMPLATE/enhancement.md
vendored
|
|
@ -1,21 +0,0 @@
|
||||||
---
|
|
||||||
name: Enhancement
|
|
||||||
about: Suggest an idea for this project
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
type: Enhancement
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
|
||||||
A clear and concise description of what you want to happen.
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context or screenshots about the feature request here.
|
|
||||||
15
.github/dependabot.yml
vendored
15
.github/dependabot.yml
vendored
|
|
@ -1,15 +0,0 @@
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
# Check for updates to GitHub Actions every month.
|
|
||||||
- package-ecosystem: "github-actions"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "monthly"
|
|
||||||
# Group all github/codeql-action/* sub-paths (init, analyze, ...) into a
|
|
||||||
# single PR so they are always bumped together. Bumping them independently
|
|
||||||
# leaves the workflow with mismatched versions across steps, which
|
|
||||||
# github/codeql-action rejects at runtime.
|
|
||||||
groups:
|
|
||||||
codeql-action:
|
|
||||||
patterns:
|
|
||||||
- "github/codeql-action/*"
|
|
||||||
48
.github/workflows/codeql.yml
vendored
48
.github/workflows/codeql.yml
vendored
|
|
@ -1,48 +0,0 @@
|
||||||
name: "CodeQL Advanced"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "main" ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ "main" ]
|
|
||||||
schedule:
|
|
||||||
- cron: '31 9 * * 4'
|
|
||||||
|
|
||||||
# Declare default permissions as read only.
|
|
||||||
permissions: read-all
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
analyze:
|
|
||||||
name: Analyze (${{ matrix.language }})
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
# required for all workflows
|
|
||||||
security-events: write
|
|
||||||
|
|
||||||
# required to fetch internal or private CodeQL packs
|
|
||||||
packages: read
|
|
||||||
|
|
||||||
# only required for workflows in private repositories
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- language: actions
|
|
||||||
build-mode: none
|
|
||||||
- language: go
|
|
||||||
build-mode: autobuild
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
|
|
||||||
with:
|
|
||||||
languages: ${{ matrix.language }}
|
|
||||||
build-mode: ${{ matrix.build-mode }}
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
|
|
||||||
with:
|
|
||||||
category: "/language:${{matrix.language}}"
|
|
||||||
74
.github/workflows/conformance.yml
vendored
74
.github/workflows/conformance.yml
vendored
|
|
@ -1,74 +0,0 @@
|
||||||
name: Conformance Tests
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
pull_request:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: conformance-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
env:
|
|
||||||
CONFORMANCE_VERSION: "0.2.0-alpha.11"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
server-conformance:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check out code
|
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
|
|
||||||
with:
|
|
||||||
go-version: "^1.26"
|
|
||||||
- name: Set up Node.js
|
|
||||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
||||||
with:
|
|
||||||
node-version: 22
|
|
||||||
- name: Build everything-server
|
|
||||||
run: go build -o everything-server ./conformance/everything-server
|
|
||||||
- name: Run conformance tests (2025-11-25, stateful)
|
|
||||||
run: |
|
|
||||||
./everything-server -http=":3001" -stateless=false &
|
|
||||||
SERVER_PID=$!
|
|
||||||
trap "kill -9 $SERVER_PID 2>/dev/null || true; wait $SERVER_PID 2>/dev/null || true" EXIT
|
|
||||||
timeout 30 bash -c 'until curl -s http://localhost:3001/mcp > /dev/null; do sleep 0.5; done'
|
|
||||||
npx -y "@modelcontextprotocol/conformance@${CONFORMANCE_VERSION}" server \
|
|
||||||
--url http://localhost:3001/mcp \
|
|
||||||
--requirements 2025-11-25 \
|
|
||||||
--expected-failures ./conformance/baseline.yml
|
|
||||||
- name: Run conformance tests (2026-07-28, stateless)
|
|
||||||
run: |
|
|
||||||
./everything-server -http=":3001" -stateless &
|
|
||||||
SERVER_PID=$!
|
|
||||||
trap "kill -9 $SERVER_PID 2>/dev/null || true; wait $SERVER_PID 2>/dev/null || true" EXIT
|
|
||||||
timeout 30 bash -c 'until curl -s http://localhost:3001/mcp > /dev/null; do sleep 0.5; done'
|
|
||||||
npx -y "@modelcontextprotocol/conformance@${CONFORMANCE_VERSION}" server \
|
|
||||||
--url http://localhost:3001/mcp \
|
|
||||||
--requirements 2026-07-28 \
|
|
||||||
--expected-failures ./conformance/baseline.yml
|
|
||||||
|
|
||||||
client-conformance:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check out code
|
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
|
|
||||||
with:
|
|
||||||
go-version: "^1.26"
|
|
||||||
- name: Set up Node.js
|
|
||||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
||||||
with:
|
|
||||||
node-version: 22
|
|
||||||
- name: Run conformance tests (full suite)
|
|
||||||
run: |
|
|
||||||
npx -y "@modelcontextprotocol/conformance@${CONFORMANCE_VERSION}" client \
|
|
||||||
--command "go run ./conformance/everything-client" \
|
|
||||||
--suite all \
|
|
||||||
--expected-failures ./conformance/baseline.yml
|
|
||||||
41
.github/workflows/docs-check.yml
vendored
41
.github/workflows/docs-check.yml
vendored
|
|
@ -1,41 +0,0 @@
|
||||||
name: Docs Check
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- 'internal/readme/**'
|
|
||||||
- 'README.md'
|
|
||||||
- 'internal/docs/**'
|
|
||||||
- 'docs/**'
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
docs-check:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
|
|
||||||
with:
|
|
||||||
go-version: "^1.26"
|
|
||||||
- name: Check out code
|
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
- name: Check docs are up-to-date
|
|
||||||
run: |
|
|
||||||
go generate ./...
|
|
||||||
if [ -n "$(git status --porcelain)" ]; then
|
|
||||||
echo "ERROR: docs are not up-to-date!"
|
|
||||||
echo ""
|
|
||||||
echo "The docs differ from what would be generated by go generate ./..."
|
|
||||||
echo "Please update internal/**/*.src.md instead of directly editing README.md or docs/ files,"
|
|
||||||
echo "then run go generate ./... to regenerate docs."
|
|
||||||
echo ""
|
|
||||||
echo "Changes:"
|
|
||||||
git status --porcelain
|
|
||||||
echo ""
|
|
||||||
echo "Diff:"
|
|
||||||
git diff
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Docs are up-to-date."
|
|
||||||
40
.github/workflows/nightly.yml
vendored
40
.github/workflows/nightly.yml
vendored
|
|
@ -1,40 +0,0 @@
|
||||||
name: Nightly
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 2 * * *' # 2 AM UTC daily
|
|
||||||
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
vulncheck:
|
|
||||||
# Don't run scheduled builds on forks.
|
|
||||||
if: github.event_name != 'schedule' || github.repository == 'modelcontextprotocol/go-sdk'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Run govulncheck
|
|
||||||
uses: golang/govulncheck-action@032d45514ae346b1db93c04b0c90b841c370344f # v1.1.0
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
go-package: ./...
|
|
||||||
|
|
||||||
conformance:
|
|
||||||
# Don't run scheduled builds on forks.
|
|
||||||
if: github.event_name != 'schedule' || github.repository == 'modelcontextprotocol/go-sdk'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check out code
|
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
|
|
||||||
with:
|
|
||||||
go-version: "^1.26"
|
|
||||||
- name: Set up Node.js
|
|
||||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
||||||
with:
|
|
||||||
node-version: "22"
|
|
||||||
- name: Run server conformance tests
|
|
||||||
run: ./scripts/server-conformance.sh
|
|
||||||
32
.github/workflows/publish-docs.yml
vendored
32
.github/workflows/publish-docs.yml
vendored
|
|
@ -1,32 +0,0 @@
|
||||||
name: Publish Docs
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions: read-all
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
- name: Configure Git Credentials
|
|
||||||
run: |
|
|
||||||
git config user.name github-actions[bot]
|
|
||||||
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
|
|
||||||
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
||||||
with:
|
|
||||||
python-version: 3.x
|
|
||||||
- run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
|
|
||||||
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
||||||
with:
|
|
||||||
key: mkdocs-material-${{ env.cache_id }}
|
|
||||||
path: .cache
|
|
||||||
restore-keys: |
|
|
||||||
mkdocs-material-
|
|
||||||
- run: pip install mkdocs-material
|
|
||||||
- run: mkdocs gh-deploy --force
|
|
||||||
38
.github/workflows/readme-check.yml
vendored
Normal file
38
.github/workflows/readme-check.yml
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
name: README Check
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'internal/readme/**'
|
||||||
|
- 'README.md'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
readme-check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Check README is up-to-date
|
||||||
|
run: |
|
||||||
|
cd internal/readme
|
||||||
|
make
|
||||||
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
|
echo "ERROR: README.md is not up-to-date!"
|
||||||
|
echo ""
|
||||||
|
echo "The README.md file differs from what would be generated by running 'make' in internal/readme/."
|
||||||
|
echo "Please update internal/readme/README.src.md instead of README.md directly,"
|
||||||
|
echo "then run 'make' in the internal/readme/ directory to regenerate README.md."
|
||||||
|
echo ""
|
||||||
|
echo "Changes:"
|
||||||
|
git status --porcelain
|
||||||
|
echo ""
|
||||||
|
echo "Diff:"
|
||||||
|
git diff
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "README.md is up-to-date"
|
||||||
78
.github/workflows/scorecard.yml
vendored
78
.github/workflows/scorecard.yml
vendored
|
|
@ -1,78 +0,0 @@
|
||||||
# This workflow uses actions that are not certified by GitHub. They are provided
|
|
||||||
# by a third-party and are governed by separate terms of service, privacy
|
|
||||||
# policy, and support documentation.
|
|
||||||
|
|
||||||
name: Scorecard supply-chain security
|
|
||||||
on:
|
|
||||||
# For Branch-Protection check. Only the default branch is supported. See
|
|
||||||
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
|
|
||||||
branch_protection_rule:
|
|
||||||
# To guarantee Maintained check is occasionally updated. See
|
|
||||||
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
|
|
||||||
schedule:
|
|
||||||
- cron: '41 23 * * 5'
|
|
||||||
push:
|
|
||||||
branches: [ "main" ]
|
|
||||||
|
|
||||||
# Declare default permissions as read only.
|
|
||||||
permissions: read-all
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
analysis:
|
|
||||||
name: Scorecard analysis
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
# `publish_results: true` only works when run from the default branch. conditional can be removed if disabled.
|
|
||||||
if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request'
|
|
||||||
permissions:
|
|
||||||
# Needed to upload the results to code-scanning dashboard.
|
|
||||||
security-events: write
|
|
||||||
# Needed to publish results and get a badge (see publish_results below).
|
|
||||||
id-token: write
|
|
||||||
# Uncomment the permissions below if installing in a private repository.
|
|
||||||
# contents: read
|
|
||||||
# actions: read
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: "Checkout code"
|
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: "Run analysis"
|
|
||||||
uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
|
|
||||||
with:
|
|
||||||
results_file: results.sarif
|
|
||||||
results_format: sarif
|
|
||||||
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
|
|
||||||
# - you want to enable the Branch-Protection check on a *public* repository, or
|
|
||||||
# - you are installing Scorecard on a *private* repository
|
|
||||||
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional.
|
|
||||||
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
|
|
||||||
|
|
||||||
# Public repositories:
|
|
||||||
# - Publish results to OpenSSF REST API for easy access by consumers
|
|
||||||
# - Allows the repository to include the Scorecard badge.
|
|
||||||
# - See https://github.com/ossf/scorecard-action#publishing-results.
|
|
||||||
# For private repositories:
|
|
||||||
# - `publish_results` will always be set to `false`, regardless
|
|
||||||
# of the value entered here.
|
|
||||||
publish_results: true
|
|
||||||
|
|
||||||
# (Optional) Uncomment file_mode if you have a .gitattributes with files marked export-ignore
|
|
||||||
# file_mode: git
|
|
||||||
|
|
||||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
|
||||||
# format to the repository Actions tab.
|
|
||||||
- name: "Upload artifact"
|
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
||||||
with:
|
|
||||||
name: SARIF file
|
|
||||||
path: results.sarif
|
|
||||||
retention-days: 5
|
|
||||||
|
|
||||||
# Upload the results to GitHub's code scanning dashboard (optional).
|
|
||||||
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
|
|
||||||
- name: "Upload to code-scanning"
|
|
||||||
uses: github/codeql-action/upload-sarif@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
|
|
||||||
with:
|
|
||||||
sarif_file: results.sarif
|
|
||||||
23
.github/workflows/test.yml
vendored
23
.github/workflows/test.yml
vendored
|
|
@ -14,11 +14,11 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@v4
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "^1.26"
|
go-version: "^1.23"
|
||||||
- name: Check formatting
|
- name: Check formatting
|
||||||
run: |
|
run: |
|
||||||
unformatted=$(gofmt -l .)
|
unformatted=$(gofmt -l .)
|
||||||
|
|
@ -31,21 +31,20 @@ jobs:
|
||||||
- name: Run Go vet
|
- name: Run Go vet
|
||||||
run: go vet ./...
|
run: go vet ./...
|
||||||
- name: Run staticcheck
|
- name: Run staticcheck
|
||||||
uses: dominikh/staticcheck-action@9716614d4101e79b4340dd97b10e54d68234e431 # v1.4.1
|
uses: dominikh/staticcheck-action@v1
|
||||||
with:
|
with:
|
||||||
install-go: false
|
version: "latest"
|
||||||
version: "v0.6.1"
|
|
||||||
|
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go: ["1.25", "1.26"]
|
go: ["1.23", "1.24", "1.25.0-rc.3"]
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@v4
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go }}
|
go-version: ${{ matrix.go }}
|
||||||
- name: Test
|
- name: Test
|
||||||
|
|
@ -55,10 +54,10 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@v4
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "1.26"
|
go-version: "1.24"
|
||||||
- name: Test with -race
|
- name: Test with -race
|
||||||
run: go test -v -race ./...
|
run: go test -v -race ./...
|
||||||
|
|
|
||||||
58
AGENTS.md
58
AGENTS.md
|
|
@ -1,58 +0,0 @@
|
||||||
# AGENTS.md
|
|
||||||
|
|
||||||
## Project Overview
|
|
||||||
|
|
||||||
This repository contains the official Go SDK for the Model Context Protocol (MCP).
|
|
||||||
The SDK is designed to be idiomatic, future-proof, and extensible.
|
|
||||||
|
|
||||||
### Key Packages
|
|
||||||
|
|
||||||
- `mcp`: The core package defining the primary APIs for constructing and using MCP clients and servers. This is where most logic resides.
|
|
||||||
- `jsonrpc`: Provides the JSON-RPC 2.0 transport layer. Use this if implementing custom transports.
|
|
||||||
- `auth`: Primitives for supporting OAuth.
|
|
||||||
- `oauthex`: Extensions to the OAuth protocol, such as Protected Resource Metadata.
|
|
||||||
- `internal`: Internal implementation details not exposed to users.
|
|
||||||
- `examples`: Example clients and servers. Use these as references for usage patterns.
|
|
||||||
|
|
||||||
## Development Setup
|
|
||||||
|
|
||||||
The project uses the standard Go toolchain.
|
|
||||||
|
|
||||||
- **Build**: `go build ./...`
|
|
||||||
- **Test**: `go test ./...`
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
- **Unit Tests**: Run `go test ./...` to run all unit tests.
|
|
||||||
- **Conformance Tests**: Use the following scripts to run the official MCP conformance tests against the SDK.
|
|
||||||
- `./scripts/server-conformance.sh` for server tests.
|
|
||||||
- `./scripts/client-conformance.sh` for client tests.
|
|
||||||
- The scripts download the latest conformance suite from npm by default.
|
|
||||||
- To get possible options pass the `--help` flag to the script.
|
|
||||||
|
|
||||||
## Development Guidelines
|
|
||||||
|
|
||||||
### Code Style
|
|
||||||
|
|
||||||
- Follow standard Go conventions (Effective Go).
|
|
||||||
- Use `gofmt` to format code.
|
|
||||||
- Add copyright headers to all new Go files:
|
|
||||||
```go
|
|
||||||
// Copyright 2025 The Go MCP SDK Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by the license
|
|
||||||
// that can be found in the LICENSE file.
|
|
||||||
```
|
|
||||||
- Do not add comments to the code unless they are really necessary:
|
|
||||||
- Prefer self-documenting code.
|
|
||||||
- Focus on the "why" not the "what" in comments.
|
|
||||||
|
|
||||||
### Documentation
|
|
||||||
|
|
||||||
- **README.md**: Do NOT edit `README.md` directly. It is generated from `internal/readme/README.src.md`.
|
|
||||||
- Edit `internal/readme/README.src.md`.
|
|
||||||
- Run `go generate ./internal/readme` to regenerate.
|
|
||||||
- Commit both files.
|
|
||||||
- **docs/**: Do NOT edit `docs/` directory directly. It is generated from files in `internal/docs`.
|
|
||||||
- Edit `internal/docs/*.src.md`.
|
|
||||||
- Run `go generate ./internal/docs` to regenerate.
|
|
||||||
- Commit files from both directories.
|
|
||||||
277
CONTRIBUTING.md
277
CONTRIBUTING.md
|
|
@ -1,4 +1,3 @@
|
||||||
<!-- Autogenerated by weave; DO NOT EDIT -->
|
|
||||||
# Contributing to the Go MCP SDK
|
# Contributing to the Go MCP SDK
|
||||||
|
|
||||||
Thank you for your interest in contributing! The Go SDK needs active
|
Thank you for your interest in contributing! The Go SDK needs active
|
||||||
|
|
@ -7,31 +6,11 @@ new and emerging use-cases. We welcome all forms of contribution, from filing
|
||||||
and reviewing issues, to contributing fixes, to proposing and implementing new
|
and reviewing issues, to contributing fixes, to proposing and implementing new
|
||||||
features.
|
features.
|
||||||
|
|
||||||
As described in the [design document](https://github.com/modelcontextprotocol/go-sdk/blob/main/design/design.md), it is important for
|
As described in the [design document](./design/design.md), it is important for
|
||||||
the MCP SDK to remain idiomatic, future-proof, and extensible. The process
|
the MCP SDK to remain idiomatic, future-proof, and extensible. The process
|
||||||
described here is intended to ensure that the SDK evolves safely and
|
described here is intended to ensure that the SDK evolves safely and
|
||||||
transparently, while adhering to these goals.
|
transparently, while adhering to these goals.
|
||||||
|
|
||||||
1. [Development setup](#development-setup)
|
|
||||||
1. [Conformance tests](#conformance-tests)
|
|
||||||
1. [Filing issues](#filing-issues)
|
|
||||||
1. [Bugs](#bugs)
|
|
||||||
1. [Proposals](#proposals)
|
|
||||||
1. [Design discussion](#design-discussion)
|
|
||||||
1. [Contributing code](#contributing-code)
|
|
||||||
1. [Adding and updating dependencies](#adding-and-updating-dependencies)
|
|
||||||
1. [Updating the README](#updating-the-readme)
|
|
||||||
1. [Versioning](#versioning)
|
|
||||||
1. [What counts as a breaking change](#what-counts-as-a-breaking-change)
|
|
||||||
1. [Deprecation](#deprecation)
|
|
||||||
1. [Timeouts](#timeouts)
|
|
||||||
1. [Code of conduct](#code-of-conduct)
|
|
||||||
1. [Governance](#governance)
|
|
||||||
1. [Working Group meetings](#working-group-meetings)
|
|
||||||
1. [Discord](#discord)
|
|
||||||
1. [Antitrust considerations](#antitrust-considerations)
|
|
||||||
|
|
||||||
|
|
||||||
## Development setup
|
## Development setup
|
||||||
|
|
||||||
This module can be built and tested using the standard Go toolchain. Run `go
|
This module can be built and tested using the standard Go toolchain. Run `go
|
||||||
|
|
@ -47,34 +26,6 @@ containing the SDK, run:
|
||||||
go work init ./project ./go-sdk
|
go work init ./project ./go-sdk
|
||||||
```
|
```
|
||||||
|
|
||||||
### Conformance tests
|
|
||||||
|
|
||||||
The SDK includes a script to run the official MCP conformance tests against the
|
|
||||||
SDK's conformance server:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
./scripts/conformance.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
By default, results are cleaned up after the script runs. To save results to a
|
|
||||||
specific directory:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
./scripts/conformance.sh --result_dir ./conformance-results
|
|
||||||
```
|
|
||||||
|
|
||||||
To run against a local checkout of the
|
|
||||||
[conformance repo](https://github.com/modelcontextprotocol/conformance) instead
|
|
||||||
of the latest npm release:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
./scripts/conformance.sh --conformance_repo ~/src/conformance
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: you must run `npm install` in the conformance repo first.
|
|
||||||
|
|
||||||
Run `./scripts/conformance.sh --help` for more options.
|
|
||||||
|
|
||||||
## Filing issues
|
## Filing issues
|
||||||
|
|
||||||
This project uses the [GitHub issue
|
This project uses the [GitHub issue
|
||||||
|
|
@ -144,38 +95,14 @@ Changes should be high quality and well tested, and should generally follow the
|
||||||
messages should follow the [format used by the Go
|
messages should follow the [format used by the Go
|
||||||
project](https://go.dev/wiki/CommitMessage).
|
project](https://go.dev/wiki/CommitMessage).
|
||||||
|
|
||||||
Unless otherwise noted, the Go source files are distributed under the license
|
Unless otherwise noted, the Go source files are distributed under the MIT-style
|
||||||
found in the LICENSE file. New contributions are licensed under Apache 2.0. All
|
license found in the LICENSE file. All Go files in the SDK should have a
|
||||||
Go files in the SDK should have a copyright header following the format below:
|
copyright header following the format below:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// Copyright 2025 The Go MCP SDK Authors. All rights reserved.
|
// Copyright 2025 The Go MCP SDK Authors. All rights reserved.
|
||||||
// Use of this source code is governed by the license
|
// Use of this source code is governed by an MIT-style
|
||||||
// that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
```
|
|
||||||
|
|
||||||
### Adding and updating dependencies
|
|
||||||
|
|
||||||
In general, the SDK tries to use as few dependencies as possible. Each new
|
|
||||||
dependency is a potential source for bugs, churn, and conflicts for our users.
|
|
||||||
Therefore, we require a [proposal](#proposals) for any new module dependency,
|
|
||||||
including upgrading an existing module to a new major version. New dependencies
|
|
||||||
should be evaluated for their stability and security, and should be
|
|
||||||
well-established in the Go ecosystem.
|
|
||||||
|
|
||||||
In general, dependencies should be for internal use by the SDK implementation,
|
|
||||||
or for testing. Do not include types from dependencies in the SDK API.
|
|
||||||
|
|
||||||
On the other hand, updating existing dependencies can be done at any time
|
|
||||||
without a proposal, as long as their major version does not change. Prefer to
|
|
||||||
update dependencies immediately following a release of the SDK, to allow as
|
|
||||||
much time as possible to find issues with the new version.
|
|
||||||
|
|
||||||
After any change to dependencies, run govulncheck to check them for
|
|
||||||
vulnerabilities.
|
|
||||||
|
|
||||||
```
|
|
||||||
go run golang.org/x/vuln/cmd/govulncheck@latest
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Updating the README
|
### Updating the README
|
||||||
|
|
@ -184,64 +111,13 @@ The top-level `README.md` file is generated from `internal/readme/README.src.md`
|
||||||
and should not be edited directly. To update the README:
|
and should not be edited directly. To update the README:
|
||||||
|
|
||||||
1. Make your changes to `internal/readme/README.src.md`
|
1. Make your changes to `internal/readme/README.src.md`
|
||||||
2. Run `go generate ./internal/readme` from the repository root to regenerate `README.md`
|
2. Run `make` in the `internal/readme/` directory to regenerate `README.md`
|
||||||
3. Commit both files together
|
3. Commit both files together
|
||||||
|
|
||||||
The CI system will automatically check that the README is up-to-date by running
|
The CI system will automatically check that the README is up-to-date by running
|
||||||
`go generate ./internal/readme` and verifying no changes result. If you see a CI failure about the
|
`make` and verifying no changes result. If you see a CI failure about the
|
||||||
README being out of sync, follow the steps above to regenerate it.
|
README being out of sync, follow the steps above to regenerate it.
|
||||||
|
|
||||||
## Versioning
|
|
||||||
|
|
||||||
The SDK follows [semantic versioning](https://semver.org), as the Go module
|
|
||||||
system requires: the major version is part of the module path, so a breaking
|
|
||||||
release would import as `github.com/modelcontextprotocol/go-sdk/v2`. A breaking
|
|
||||||
change therefore cannot reach existing users by accident; they have to change
|
|
||||||
their import path to receive one.
|
|
||||||
|
|
||||||
This policy covers the exported API of the SDK's importable packages — `mcp`,
|
|
||||||
`jsonrpc`, `auth`, `auth/extauth` and `oauthex`. Everything under `internal/`
|
|
||||||
is not importable outside the module and may change in any release.
|
|
||||||
|
|
||||||
Which MCP spec revisions each SDK version speaks is documented in the
|
|
||||||
[README](README.md#version-compatibility). Support for a new revision arrives
|
|
||||||
in a minor release; dropping a revision the SDK previously negotiated is a
|
|
||||||
breaking change.
|
|
||||||
|
|
||||||
### What counts as a breaking change
|
|
||||||
|
|
||||||
- Removing or renaming an exported identifier.
|
|
||||||
- Changing the signature of an exported function or method.
|
|
||||||
- Removing or renaming a field of an exported struct, or changing its type.
|
|
||||||
- Adding a method to an exported interface that users are meant to implement.
|
|
||||||
- Changing documented behavior that callers could reasonably rely on.
|
|
||||||
- Dropping support for an MCP spec revision.
|
|
||||||
|
|
||||||
The following are not breaking, and may land in a minor or patch release:
|
|
||||||
|
|
||||||
- Adding an exported identifier, or a field to an exported struct.
|
|
||||||
- Fixing behavior that contradicted its documentation.
|
|
||||||
- Refactoring that leaves the exported API unchanged.
|
|
||||||
- Adding support for a new MCP spec revision.
|
|
||||||
- Raising the minimum Go version to one still supported upstream, per the
|
|
||||||
[README](README.md#version-compatibility).
|
|
||||||
|
|
||||||
### Deprecation
|
|
||||||
|
|
||||||
An API on its way out is marked with a `// Deprecated:` comment naming its
|
|
||||||
replacement, which `gopls` and `staticcheck` surface at call sites. It keeps
|
|
||||||
working until the next major version, since removing it would itself be a
|
|
||||||
breaking change.
|
|
||||||
|
|
||||||
Protocol features that the *specification* deprecates are a separate matter and
|
|
||||||
follow the spec's timeline rather than this policy. The features deprecated by
|
|
||||||
[SEP-2577](https://modelcontextprotocol.io/seps/2577-deprecate-roots-sampling-and-logging)
|
|
||||||
— roots, sampling and logging — remain supported for at least twelve months, as
|
|
||||||
noted in the [README](README.md#version-compatibility).
|
|
||||||
|
|
||||||
Any change to the exported API, breaking or not, goes through the
|
|
||||||
[proposal process](#proposals).
|
|
||||||
|
|
||||||
## Timeouts
|
## Timeouts
|
||||||
|
|
||||||
If a contributor hasn't responded to issue questions or PR comments in two weeks,
|
If a contributor hasn't responded to issue questions or PR comments in two weeks,
|
||||||
|
|
@ -283,9 +159,134 @@ GitHub discussions, or public steering meetings.
|
||||||
|
|
||||||
### Antitrust considerations
|
### Antitrust considerations
|
||||||
|
|
||||||
The goal of this repository is to provide a robust and complete Go
|
It is important that the SDK avoids bias toward specific integration paths or
|
||||||
implementation of the model context protocol, in an open and transparent
|
providers. The antitrust policy below details precise terms that should be
|
||||||
manner, without bias toward specific integration paths or providers. To that
|
adhered to in order to ensure that the evolution and governance of the SDK remain
|
||||||
end, the model context protocol organization's [antitrust
|
procompetitive.
|
||||||
policy](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/ANTITRUST.md)
|
|
||||||
applies to all participation in this project.
|
### Antitrust policy
|
||||||
|
|
||||||
|
Note: all changes to the policy in this section must be approved by legal
|
||||||
|
counsel.
|
||||||
|
|
||||||
|
The Go+Anthropic MCP SDK Working Group (the “Working Group”) is established to
|
||||||
|
guide and review technical contributions to an open-source Go software
|
||||||
|
development kit (“SDK”) for the Model Context Protocol (“MCP”). The Working Group
|
||||||
|
and its members are committed to operating for procompetitive purposes that
|
||||||
|
accelerate AI development and benefit businesses and consumers. This
|
||||||
|
collaboration is focused on technical and infrastructure objectives – namely,
|
||||||
|
developing and maintaining a neutral, open-source, and MIT-licensed library.
|
||||||
|
Google and Anthropic, as well as other stakeholders, participate with the
|
||||||
|
understanding that the Working Group’s sole purpose is to improve interoperability
|
||||||
|
and innovation in the MCP ecosystem.
|
||||||
|
|
||||||
|
Antitrust law recognizes that when competitors collaborate for valid reasons
|
||||||
|
(e.g., joint R&D or standard-setting), such collaborations can be
|
||||||
|
procompetitive. This Antitrust Compliance Policy (the “Policy”) therefore
|
||||||
|
outlines guidelines and safeguards to ensure the collaboration remains focused
|
||||||
|
on its technical mission.
|
||||||
|
|
||||||
|
The Policy applies to all Working Group activities and communications,
|
||||||
|
including official meetings, subcommittee discussions, emails, shared
|
||||||
|
documents, and any other interactions under the Working Group’s auspices (e.g.,
|
||||||
|
group chats, version control systems). It applies to all participants from
|
||||||
|
Google, Anthropic, and any other member organizations or independent
|
||||||
|
contributors involved in the Working Group. Each participating entity should
|
||||||
|
ensure its representatives understand and uphold these rules. By participating,
|
||||||
|
members agree to follow the Policy in both letter and spirit.
|
||||||
|
|
||||||
|
#### Governance Procedures and Principles
|
||||||
|
|
||||||
|
- **Participant Guidelines.** Participants should generally be limited to
|
||||||
|
individuals in technical roles who are directly involved in the MCP SDK
|
||||||
|
project. These participants should not be key decision-makers in their
|
||||||
|
company’s AI commercial strategy, sales, marketing, pricing, or other
|
||||||
|
competitively or strategically sensitive business planning.
|
||||||
|
- **Agenda Preparation.** A written agenda should be circulated before each
|
||||||
|
Working Group meeting. Agenda items should focus on the SDK’s technical
|
||||||
|
development, maintenance, or documentation. Where appropriate, counsel should
|
||||||
|
review the agenda prior to circulation to ensure compliance with the Policy.
|
||||||
|
- **Policy Reminder at Start.** Meetings should begin with a brief antitrust
|
||||||
|
compliance reminder with reference to the Policy.
|
||||||
|
- **Minutes.** Meetings will be minuted by a designated participant, and
|
||||||
|
neutrally record attendees, roles, topics discussed, action items, and
|
||||||
|
outcomes. Draft minutes will be circulated to all participants.
|
||||||
|
- **Documentation and Transparency.** Working Group outputs are intended
|
||||||
|
for public release. Significant design decisions and discussion outcomes
|
||||||
|
should be documented publicly. If a topic cannot be safely disclosed
|
||||||
|
publicly, it likely does not belong in this forum.
|
||||||
|
- **Independence of Decision-Making.** All participants and their companies
|
||||||
|
retain complete independence in their own business decisions and competitive
|
||||||
|
strategies outside of the MCP SDK project. Nothing in this collaboration
|
||||||
|
restricts or influences how each company operates its commercial business.
|
||||||
|
|
||||||
|
### Information Exchange Guidelines
|
||||||
|
|
||||||
|
**Appropriate Topics.** Working Group members are anticipated to remain within the
|
||||||
|
project’s technical scope. In general, discussions should focus on improving
|
||||||
|
the Go SDK for MCP in a transparent, collaborative, and non-exclusive manner.
|
||||||
|
The following topics are appropriate and expected:
|
||||||
|
|
||||||
|
- **Software Design and Architecture:** Implementation of MCP features in Go,
|
||||||
|
API design, code structure, testing frameworks, performance considerations,
|
||||||
|
compatibility issues, and security concerns.
|
||||||
|
- **Technical Contributions and Bug Fixes:** Review of code contributions,
|
||||||
|
debugging problems, and handling feature requests.
|
||||||
|
- **Documentation and Open-Source Logistics:** Discussions of project
|
||||||
|
documentation, changelogs, versioning strategy, and managing contributions.
|
||||||
|
- **Standards and Interoperability:** Ensuring SDK compliance with MCP or other
|
||||||
|
open technical standards. Any standardization effort should be open,
|
||||||
|
voluntary, and tailored to promote interoperability.
|
||||||
|
- **Public Information:** Any public information relevant to the Working
|
||||||
|
Group’s technical work (e.g., published research, open-source code from
|
||||||
|
outside projects, publicly documented API specs).
|
||||||
|
|
||||||
|
**Inappropriate Topics.** To ensure compliance with antitrust law and maintain
|
||||||
|
the open character of the collaboration, the following subjects should not be
|
||||||
|
discussed in Working Group meetings, side conversations, or related communications:
|
||||||
|
|
||||||
|
- **Pricing and Commercial Terms:** Do not discuss prices, pricing strategy,
|
||||||
|
discounting, or future pricing plans for Claude, Gemini, or any other AI
|
||||||
|
product or service provided by Working Group members.
|
||||||
|
- **Sales or Output:** Avoid sharing sales volumes, revenue, customer counts,
|
||||||
|
market shares, production plans, or any business performance metrics.
|
||||||
|
- **Product Roadmaps (beyond SDK):** Do not disclose internal plans for AI
|
||||||
|
model development, feature rollouts, or future commercialization strategies.
|
||||||
|
- **Customers or Markets:** No discussions of which customers, industries, or
|
||||||
|
geographies the parties will pursue or avoid.
|
||||||
|
- **Customer or Supplier Details:** Do not share specifics of contracts,
|
||||||
|
negotiations, or relationships with commercial partners.
|
||||||
|
- **Non-SDK Proprietary Tech:** Keep discussions focused on the open SDK.
|
||||||
|
Sharing of information should be limited to what is needed to achieve the
|
||||||
|
Working Group’s goals. Each party’s internal model architectures, fine-tuning
|
||||||
|
approaches, or training methods unrelated to the project should not be
|
||||||
|
disclosed.
|
||||||
|
- **HR or Labor Matters:** No discussions about wages, hiring plans, or
|
||||||
|
policies toward employees.
|
||||||
|
|
||||||
|
#### Enforcement and Support
|
||||||
|
|
||||||
|
- **Shared Responsibility.** All Working Group participants share responsibility
|
||||||
|
for upholding the Policy. While legal counsel can provide support, day-to-day
|
||||||
|
compliance is a function of culture and practice.
|
||||||
|
- **Designated Legal Contacts.** Each participating entity should designate a
|
||||||
|
legal point of contact responsible for reviewing meeting materials (e.g.,
|
||||||
|
agendas, minutes) and fielding questions about compliance. These contacts
|
||||||
|
should be included in the Working Group distribution list for all official
|
||||||
|
materials and should be consulted in advance of any meetings where sensitive
|
||||||
|
topics may arise.
|
||||||
|
- **Final Note.** The Policy is not meant to chill legitimate technical
|
||||||
|
collaboration. It is meant to ensure that the Working Group can focus on its
|
||||||
|
mission without creating unnecessary legal risk or attracting regulator
|
||||||
|
scrutiny. Participants who follow the Policy and avoid Inappropriate Topics
|
||||||
|
will remain squarely within the procompetitive zone.
|
||||||
|
|
||||||
|
### Ongoing evaluation
|
||||||
|
|
||||||
|
On an ongoing basis, the administrators of the SDK will evaluate whether it is
|
||||||
|
keeping pace with changes to the MCP spec and meeting its goals of openness and
|
||||||
|
transparency. If it is not meeting these goals, either because it exceeds the
|
||||||
|
bandwidth of its current Working Group, or because the processes here are
|
||||||
|
inadequate, these processes will be re-evaluated by the Working Group. At this
|
||||||
|
time, the Working Group set may be expanded to include additional community
|
||||||
|
members, based on their history of strong contribution.
|
||||||
|
|
|
||||||
197
LICENSE
197
LICENSE
|
|
@ -1,193 +1,6 @@
|
||||||
The MCP project is undergoing a licensing transition from the MIT License to the Apache License, Version 2.0 ("Apache-2.0"). All new code and specification contributions to the project are licensed under Apache-2.0. Documentation contributions (excluding specifications) are licensed under CC-BY-4.0.
|
|
||||||
|
|
||||||
Contributions for which relicensing consent has been obtained are licensed under Apache-2.0. Contributions made by authors who originally licensed their work under the MIT License and who have not yet granted explicit permission to relicense remain licensed under the MIT License.
|
|
||||||
|
|
||||||
No rights beyond those granted by the applicable original license are conveyed for such contributions.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright
|
|
||||||
owner or by an individual or Legal Entity authorized to submit on behalf
|
|
||||||
of the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2024-2025 Model Context Protocol a Series of LF Projects, LLC.
|
Copyright (c) 2025 Go MCP SDK Authors
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
@ -206,11 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Creative Commons Attribution 4.0 International (CC-BY-4.0)
|
|
||||||
|
|
||||||
Documentation in this project (excluding specifications) is licensed under
|
|
||||||
CC-BY-4.0. See https://creativecommons.org/licenses/by/4.0/legalcode for
|
|
||||||
the full license text.
|
|
||||||
|
|
|
||||||
176
README.md
176
README.md
|
|
@ -1,107 +1,57 @@
|
||||||
<!-- Autogenerated by weave; DO NOT EDIT -->
|
<!-- Autogenerated by weave; DO NOT EDIT -->
|
||||||
# MCP Go SDK
|
# MCP Go SDK v0.3.0
|
||||||
|
|
||||||
[](https://codespaces.new/modelcontextprotocol/go-sdk)
|
[](https://codespaces.new/modelcontextprotocol/go-sdk)
|
||||||
|
|
||||||
|
***BREAKING CHANGES***
|
||||||
|
|
||||||
|
This version contains breaking changes.
|
||||||
|
See the [release notes](
|
||||||
|
https://github.com/modelcontextprotocol/go-sdk/releases/tag/v0.3.0) for details.
|
||||||
|
|
||||||
[](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk)
|
[](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk)
|
||||||
[](https://scorecard.dev/viewer/?uri=github.com/modelcontextprotocol/go-sdk)
|
|
||||||
|
|
||||||
This repository contains an implementation of the official Go software
|
This repository contains an unreleased implementation of the official Go
|
||||||
development kit (SDK) for the Model Context Protocol (MCP).
|
software development kit (SDK) for the Model Context Protocol (MCP).
|
||||||
|
|
||||||
## Package / Feature documentation
|
> [!WARNING]
|
||||||
|
> The SDK is not yet at v1.0.0 and may still be subject to incompatible API
|
||||||
|
> changes. We aim to tag v1.0.0 in September, 2025. See
|
||||||
|
> https://github.com/modelcontextprotocol/go-sdk/issues/328 for details.
|
||||||
|
|
||||||
The SDK consists of several importable packages:
|
## Design
|
||||||
|
|
||||||
|
The design doc for this SDK is at [design.md](./design/design.md), which was
|
||||||
|
initially reviewed at
|
||||||
|
[modelcontextprotocol/discussions/364](https://github.com/orgs/modelcontextprotocol/discussions/364).
|
||||||
|
|
||||||
|
Further design discussion should occur in
|
||||||
|
[issues](https://github.com/modelcontextprotocol/go-sdk/issues) (for concrete
|
||||||
|
proposals) or
|
||||||
|
[discussions](https://github.com/modelcontextprotocol/go-sdk/discussions) for
|
||||||
|
open-ended discussion. See [CONTRIBUTING.md](/CONTRIBUTING.md) for details.
|
||||||
|
|
||||||
|
## Package documentation
|
||||||
|
|
||||||
|
The SDK consists of three importable packages:
|
||||||
|
|
||||||
- The
|
- The
|
||||||
[`github.com/modelcontextprotocol/go-sdk/mcp`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp)
|
[`github.com/modelcontextprotocol/go-sdk/mcp`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp)
|
||||||
package defines the primary APIs for constructing and using MCP clients and
|
package defines the primary APIs for constructing and using MCP clients and
|
||||||
servers.
|
servers.
|
||||||
|
- The
|
||||||
|
[`github.com/modelcontextprotocol/go-sdk/jsonschema`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/jsonschema)
|
||||||
|
package provides an implementation of [JSON
|
||||||
|
Schema](https://json-schema.org/), used for MCP tool input and output schema.
|
||||||
- The
|
- The
|
||||||
[`github.com/modelcontextprotocol/go-sdk/jsonrpc`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/jsonrpc) package is for users implementing
|
[`github.com/modelcontextprotocol/go-sdk/jsonrpc`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/jsonrpc) package is for users implementing
|
||||||
their own transports.
|
their own transports.
|
||||||
- The
|
|
||||||
[`github.com/modelcontextprotocol/go-sdk/auth`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth)
|
|
||||||
package provides some primitives for supporting OAuth.
|
|
||||||
- The
|
|
||||||
[`github.com/modelcontextprotocol/go-sdk/oauthex`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/oauthex)
|
|
||||||
package provides extensions to the OAuth protocol, such as ProtectedResourceMetadata.
|
|
||||||
|
|
||||||
The SDK endeavors to implement the full MCP spec. The [`docs/`](/docs/) directory
|
|
||||||
contains feature documentation, mapping the MCP spec to the packages above.
|
|
||||||
|
|
||||||
## Version Compatibility
|
## Example
|
||||||
|
|
||||||
The following table shows which versions of the Go SDK support which versions of the MCP specification:
|
In this example, an MCP client communicates with an MCP server running in a
|
||||||
|
sidecar process:
|
||||||
| SDK Version | Latest MCP Spec | All Supported MCP Specs |
|
|
||||||
|-----------------|-------------------|------------------------------------------------------------------|
|
|
||||||
| v1.7.0+ | 2026-07-28 | 2026-07-28, 2025-11-25\*, 2025-06-18, 2025-03-26, 2024-11-05 |
|
|
||||||
| v1.4.0 - v1.6.1 | 2025-11-25\* | 2025-11-25\*, 2025-06-18, 2025-03-26, 2024-11-05 |
|
|
||||||
| v1.2.0 - v1.3.1 | 2025-11-25\*\* | 2025-11-25\*\*, 2025-06-18, 2025-03-26, 2024-11-05 |
|
|
||||||
| v1.0.0 - v1.1.0 | 2025-06-18 | 2025-06-18, 2025-03-26, 2024-11-05 |
|
|
||||||
|
|
||||||
\* Client side OAuth has experimental support.
|
|
||||||
|
|
||||||
\*\* Partial support for 2025-11-25 (client side OAuth and Sampling with tools not available).
|
|
||||||
|
|
||||||
The roots, sampling, and logging features are deprecated as of protocol version
|
|
||||||
2026-07-28 by
|
|
||||||
[SEP-2577](https://modelcontextprotocol.io/seps/2577-deprecate-roots-sampling-and-logging).
|
|
||||||
The SDK continues to support them for compatibility during the deprecation
|
|
||||||
window (at least twelve months). See the individual feature documentation for
|
|
||||||
migration guidance.
|
|
||||||
|
|
||||||
New releases of the SDK target only supported versions of Go. See
|
|
||||||
https://go.dev/doc/devel/release#policy for more information.
|
|
||||||
|
|
||||||
## Getting started
|
|
||||||
|
|
||||||
To get started creating an MCP server, create an `mcp.Server` instance, add
|
|
||||||
features to it, and then run it over an `mcp.Transport`. For example, this
|
|
||||||
server adds a single simple tool, and then connects it to clients over
|
|
||||||
stdin/stdout:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Input struct {
|
|
||||||
Name string `json:"name" jsonschema:"the name of the person to greet"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Output struct {
|
|
||||||
Greeting string `json:"greeting" jsonschema:"the greeting to tell to the user"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func SayHi(ctx context.Context, req *mcp.CallToolRequest, input Input) (
|
|
||||||
*mcp.CallToolResult,
|
|
||||||
Output,
|
|
||||||
error,
|
|
||||||
) {
|
|
||||||
return nil, Output{Greeting: "Hi " + input.Name}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// Create a server with a single tool.
|
|
||||||
server := mcp.NewServer(&mcp.Implementation{Name: "greeter", Version: "v1.0.0"}, nil)
|
|
||||||
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
|
|
||||||
// Run the server over stdin/stdout, until the client disconnects.
|
|
||||||
if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
To communicate with that server, create an `mcp.Client` and connect it to the
|
|
||||||
corresponding server, by running the server command and communicating over its
|
|
||||||
stdin/stdout:
|
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
@ -120,7 +70,7 @@ func main() {
|
||||||
// Create a new client, with no features.
|
// Create a new client, with no features.
|
||||||
client := mcp.NewClient(&mcp.Implementation{Name: "mcp-client", Version: "v1.0.0"}, nil)
|
client := mcp.NewClient(&mcp.Implementation{Name: "mcp-client", Version: "v1.0.0"}, nil)
|
||||||
|
|
||||||
// Connect to a server over stdin/stdout.
|
// Connect to a server over stdin/stdout
|
||||||
transport := &mcp.CommandTransport{Command: exec.Command("myserver")}
|
transport := &mcp.CommandTransport{Command: exec.Command("myserver")}
|
||||||
session, err := client.Connect(ctx, transport, nil)
|
session, err := client.Connect(ctx, transport, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -146,26 +96,54 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The [`examples/`](/examples/) directory contains more example clients and
|
Here's an example of the corresponding server component, which communicates
|
||||||
servers.
|
with its client over stdin/stdout:
|
||||||
|
|
||||||
## Contributing
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
We welcome contributions to the SDK! Please see
|
import (
|
||||||
[CONTRIBUTING.md](/CONTRIBUTING.md) for details of how to contribute.
|
"context"
|
||||||
|
"log"
|
||||||
|
|
||||||
## Acknowledgements / Alternatives
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||||
|
)
|
||||||
|
|
||||||
Several third party Go MCP SDKs inspired the development and design of this
|
type HiParams struct {
|
||||||
official SDK, and continue to be viable alternatives, notably
|
Name string `json:"name" jsonschema:"the name of the person to greet"`
|
||||||
[mcp-go](https://github.com/mark3labs/mcp-go), originally authored by Ed Zynda.
|
}
|
||||||
We are grateful to Ed as well as the other contributors to mcp-go, and to
|
|
||||||
authors and contributors of other SDKs such as
|
func SayHi(ctx context.Context, req *mcp.CallToolRequest, args HiParams) (*mcp.CallToolResult, any, error) {
|
||||||
|
return &mcp.CallToolResult{
|
||||||
|
Content: []mcp.Content{&mcp.TextContent{Text: "Hi " + args.Name}},
|
||||||
|
}, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Create a server with a single tool.
|
||||||
|
server := mcp.NewServer(&mcp.Implementation{Name: "greeter", Version: "v1.0.0"}, nil)
|
||||||
|
|
||||||
|
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
|
||||||
|
// Run the server over stdin/stdout, until the client disconnects
|
||||||
|
if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The [`examples/`](/examples/) directory contains more example clients and servers.
|
||||||
|
|
||||||
|
## Acknowledgements
|
||||||
|
|
||||||
|
Several existing Go MCP SDKs inspired the development and design of this
|
||||||
|
official SDK, notably [mcp-go](https://github.com/mark3labs/mcp-go), authored
|
||||||
|
by Ed Zynda. We are grateful to Ed as well as the other contributors to mcp-go,
|
||||||
|
and to authors and contributors of other SDKs such as
|
||||||
[mcp-golang](https://github.com/metoro-io/mcp-golang) and
|
[mcp-golang](https://github.com/metoro-io/mcp-golang) and
|
||||||
[go-mcp](https://github.com/ThinkInAIXYZ/go-mcp). Thanks to their work, there
|
[go-mcp](https://github.com/ThinkInAIXYZ/go-mcp). Thanks to their work, there
|
||||||
is a thriving ecosystem of Go MCP clients and servers.
|
is a thriving ecosystem of Go MCP clients and servers.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under Apache 2.0 for new contributions, with existing
|
This project is licensed under the MIT License - see the [LICENSE](./LICENSE)
|
||||||
code under MIT - see the [LICENSE](./LICENSE) file for details.
|
file for details.
|
||||||
|
|
|
||||||
32
ROADMAP.md
32
ROADMAP.md
|
|
@ -1,32 +0,0 @@
|
||||||
# MCP Go SDK Roadmap
|
|
||||||
|
|
||||||
## Current focus
|
|
||||||
|
|
||||||
The following items are planned for the next release (v1.4.0):
|
|
||||||
|
|
||||||
- **SEP-1730: Tier 1 SDK support** (https://github.com/modelcontextprotocol/go-sdk/issues/675)
|
|
||||||
- Description: We aim to be rated as Tier 1 SDK.
|
|
||||||
- **SEP-1577: support Sampling With Tools** (https://github.com/modelcontextprotocol/go-sdk/issues/629)
|
|
||||||
- Description: Sampling with Tools is the last SEP to be implemented
|
|
||||||
to implement the full scope of 2025-11-25 MCP specification.
|
|
||||||
- **OAuth Support** (https://github.com/modelcontextprotocol/go-sdk/issues/19)
|
|
||||||
- Description: Client-side OAuth support is the last feature
|
|
||||||
needed to provide the Authorization solution as per specification.
|
|
||||||
|
|
||||||
## Future work
|
|
||||||
|
|
||||||
Once the tiering system is in place, we will draft a new roadmap to describe
|
|
||||||
our long-term strategy. It may include experimental features or extensions
|
|
||||||
to the MCP specification listed in the next section.
|
|
||||||
|
|
||||||
## Experimental features
|
|
||||||
|
|
||||||
- **SEP-1686 (experimental): Implement Tasks** (https://github.com/modelcontextprotocol/go-sdk/issues/626)
|
|
||||||
|
|
||||||
## Extensions
|
|
||||||
|
|
||||||
### Authorization extensions (https://github.com/modelcontextprotocol/ext-auth)
|
|
||||||
|
|
||||||
- **ext-auth: Enterprise Managed Authorization** (https://github.com/modelcontextprotocol/go-sdk/issues/628)
|
|
||||||
- Status: In progress - https://github.com/modelcontextprotocol/go-sdk/pull/770
|
|
||||||
- **ext-auth: OAuth Client Credentials** (https://github.com/modelcontextprotocol/go-sdk/issues/627)
|
|
||||||
21
SECURITY.md
21
SECURITY.md
|
|
@ -1,21 +0,0 @@
|
||||||
# Security Policy
|
|
||||||
|
|
||||||
Thank you for helping keep the Model Context Protocol and its ecosystem secure.
|
|
||||||
|
|
||||||
## Reporting Security Issues
|
|
||||||
|
|
||||||
If you discover a security vulnerability in this repository, please report it through
|
|
||||||
the [GitHub Security Advisory process](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability)
|
|
||||||
for this repository.
|
|
||||||
|
|
||||||
Please **do not** report security vulnerabilities through public GitHub issues, discussions,
|
|
||||||
or pull requests.
|
|
||||||
|
|
||||||
## What to Include
|
|
||||||
|
|
||||||
To help us triage and respond quickly, please include:
|
|
||||||
|
|
||||||
- A description of the vulnerability
|
|
||||||
- Steps to reproduce the issue
|
|
||||||
- The potential impact
|
|
||||||
- Any suggested fixes (optional)
|
|
||||||
129
auth/auth.go
129
auth/auth.go
|
|
@ -6,39 +6,27 @@ package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/modelcontextprotocol/go-sdk/oauthex"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TokenInfo holds information from a bearer token.
|
// TokenInfo holds information from a bearer token.
|
||||||
type TokenInfo struct {
|
type TokenInfo struct {
|
||||||
Scopes []string
|
Scopes []string
|
||||||
Expiration time.Time
|
Expiration time.Time
|
||||||
// UserID is an optional identifier for the authenticated user.
|
// TODO: add standard JWT fields
|
||||||
// If set by a TokenVerifier, it can be used by transports to prevent
|
Extra map[string]any
|
||||||
// session hijacking by ensuring that all requests for a given session
|
|
||||||
// come from the same user.
|
|
||||||
UserID string
|
|
||||||
Extra map[string]any
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The error that a TokenVerifier should return if the token cannot be verified.
|
// The error that a TokenVerifier should return if the token cannot be verified.
|
||||||
var ErrInvalidToken = errors.New("invalid token")
|
var ErrInvalidToken = errors.New("invalid token")
|
||||||
|
|
||||||
// The error that a TokenVerifier should return for OAuth-specific protocol errors.
|
|
||||||
var ErrOAuth = errors.New("oauth error")
|
|
||||||
|
|
||||||
// A TokenVerifier checks the validity of a bearer token, and extracts information
|
// A TokenVerifier checks the validity of a bearer token, and extracts information
|
||||||
// from it. If verification fails, it should return an error that unwraps to ErrInvalidToken.
|
// from it. If verification fails, it should return an error that unwraps to ErrInvalidToken.
|
||||||
// The HTTP request is provided in case verifying the token involves checking it.
|
type TokenVerifier func(ctx context.Context, token string) (*TokenInfo, error)
|
||||||
type TokenVerifier func(ctx context.Context, token string, req *http.Request) (*TokenInfo, error)
|
|
||||||
|
|
||||||
// RequireBearerTokenOptions are options for [RequireBearerToken].
|
// RequireBearerTokenOptions are options for [RequireBearerToken].
|
||||||
type RequireBearerTokenOptions struct {
|
type RequireBearerTokenOptions struct {
|
||||||
|
|
@ -47,34 +35,6 @@ type RequireBearerTokenOptions struct {
|
||||||
ResourceMetadataURL string
|
ResourceMetadataURL string
|
||||||
// The required scopes.
|
// The required scopes.
|
||||||
Scopes []string
|
Scopes []string
|
||||||
// AllowMissingExpiration opts the middleware out of the
|
|
||||||
// `tokenInfo.Expiration.IsZero()` reject. Default false preserves the
|
|
||||||
// existing strict behaviour (every TokenInfo must carry an Expiration).
|
|
||||||
//
|
|
||||||
// Some IdPs emit session-bound bearer tokens that do not carry a standalone
|
|
||||||
// `exp` claim — the token's lifetime is bounded by an external session and
|
|
||||||
// is not advertised in-band. Resource servers integrating with such IdPs
|
|
||||||
// need to opt in to validating the rest of the token (scopes, signature
|
|
||||||
// via the verifier callback, etc.) without requiring the expiration field
|
|
||||||
// to be present.
|
|
||||||
//
|
|
||||||
// When enabled, the verifier is still responsible for any session-level
|
|
||||||
// validity check it can perform; this option only relaxes the middleware's
|
|
||||||
// own expiration enforcement.
|
|
||||||
AllowMissingExpiration bool
|
|
||||||
// ClockSkew bounds the tolerance applied to a token's Expiration when
|
|
||||||
// deciding whether it has elapsed. A token is rejected only if
|
|
||||||
// Expiration + ClockSkew is before the current time. Zero (the default)
|
|
||||||
// preserves strict comparison: any expired token is rejected immediately.
|
|
||||||
//
|
|
||||||
// Resource servers running behind a CDN, in distributed deployments, or
|
|
||||||
// communicating with an authorization server whose clock drifts a few
|
|
||||||
// seconds (common with cloud-managed IdPs) need a small positive value
|
|
||||||
// here to avoid rejecting tokens that are valid by the issuer's clock
|
|
||||||
// but momentarily appear expired by the verifier's. The same tolerance
|
|
||||||
// guards against an issuer's clock running slightly fast at /token
|
|
||||||
// issuance time.
|
|
||||||
ClockSkew time.Duration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type tokenInfoKey struct{}
|
type tokenInfoKey struct{}
|
||||||
|
|
@ -99,20 +59,11 @@ func RequireBearerToken(verifier TokenVerifier, opts *RequireBearerTokenOptions)
|
||||||
|
|
||||||
return func(handler http.Handler) http.Handler {
|
return func(handler http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
tokenInfo, errmsg, code := verify(r, verifier, opts)
|
tokenInfo, errmsg, code := verify(r.Context(), verifier, opts, r.Header.Get("Authorization"))
|
||||||
if code != 0 {
|
if code != 0 {
|
||||||
if code == http.StatusUnauthorized || code == http.StatusForbidden {
|
if code == http.StatusUnauthorized || code == http.StatusForbidden {
|
||||||
if opts != nil {
|
if opts != nil && opts.ResourceMetadataURL != "" {
|
||||||
var params []string
|
w.Header().Add("WWW-Authenticate", "Bearer resource_metadata="+opts.ResourceMetadataURL)
|
||||||
if opts.ResourceMetadataURL != "" {
|
|
||||||
params = append(params, fmt.Sprintf("resource_metadata=%q", opts.ResourceMetadataURL))
|
|
||||||
}
|
|
||||||
if len(opts.Scopes) > 0 {
|
|
||||||
params = append(params, fmt.Sprintf("scope=%q", strings.Join(opts.Scopes, " ")))
|
|
||||||
}
|
|
||||||
if len(params) > 0 {
|
|
||||||
w.Header().Add("WWW-Authenticate", "Bearer "+strings.Join(params, ", "))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
http.Error(w, errmsg, code)
|
http.Error(w, errmsg, code)
|
||||||
|
|
@ -124,28 +75,24 @@ func RequireBearerToken(verifier TokenVerifier, opts *RequireBearerTokenOptions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func verify(req *http.Request, verifier TokenVerifier, opts *RequireBearerTokenOptions) (_ *TokenInfo, errmsg string, code int) {
|
func verify(ctx context.Context, verifier TokenVerifier, opts *RequireBearerTokenOptions, authHeader string) (_ *TokenInfo, errmsg string, code int) {
|
||||||
// Extract bearer token.
|
// Extract bearer token.
|
||||||
authHeader := req.Header.Get("Authorization")
|
|
||||||
fields := strings.Fields(authHeader)
|
fields := strings.Fields(authHeader)
|
||||||
if len(fields) != 2 || strings.ToLower(fields[0]) != "bearer" {
|
if len(fields) != 2 || strings.ToLower(fields[0]) != "bearer" {
|
||||||
return nil, "no bearer token", http.StatusUnauthorized
|
return nil, "no bearer token", http.StatusUnauthorized
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the token and get information from it.
|
// Verify the token and get information from it.
|
||||||
tokenInfo, err := verifier(req.Context(), fields[1], req)
|
tokenInfo, err := verifier(ctx, fields[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, ErrInvalidToken) {
|
if errors.Is(err, ErrInvalidToken) {
|
||||||
return nil, err.Error(), http.StatusUnauthorized
|
return nil, err.Error(), http.StatusUnauthorized
|
||||||
}
|
}
|
||||||
if errors.Is(err, ErrOAuth) {
|
// TODO: the TS SDK distinguishes another error, OAuthError, and returns a 400.
|
||||||
return nil, err.Error(), http.StatusBadRequest
|
// Investigate how that works.
|
||||||
}
|
// See typescript-sdk/src/server/auth/middleware/bearerAuth.ts.
|
||||||
return nil, err.Error(), http.StatusInternalServerError
|
return nil, err.Error(), http.StatusInternalServerError
|
||||||
}
|
}
|
||||||
if tokenInfo == nil {
|
|
||||||
return nil, "token validation failed", http.StatusInternalServerError
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check scopes. All must be present.
|
// Check scopes. All must be present.
|
||||||
if opts != nil {
|
if opts != nil {
|
||||||
|
|
@ -157,58 +104,12 @@ func verify(req *http.Request, verifier TokenVerifier, opts *RequireBearerTokenO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts == nil {
|
// Check expiration.
|
||||||
opts = &RequireBearerTokenOptions{}
|
|
||||||
}
|
|
||||||
// Check expiration, with optional clock-skew tolerance. Skew only applies
|
|
||||||
// when an expiration is present; a missing expiration is governed solely by
|
|
||||||
// AllowMissingExpiration.
|
|
||||||
if tokenInfo.Expiration.IsZero() {
|
if tokenInfo.Expiration.IsZero() {
|
||||||
if !opts.AllowMissingExpiration {
|
return nil, "token missing expiration", http.StatusUnauthorized
|
||||||
return nil, "token missing expiration", http.StatusUnauthorized
|
}
|
||||||
}
|
if tokenInfo.Expiration.Before(time.Now()) {
|
||||||
} else if tokenInfo.Expiration.Add(opts.ClockSkew).Before(time.Now()) {
|
|
||||||
return nil, "token expired", http.StatusUnauthorized
|
return nil, "token expired", http.StatusUnauthorized
|
||||||
}
|
}
|
||||||
return tokenInfo, "", 0
|
return tokenInfo, "", 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProtectedResourceMetadataHandler returns an http.Handler that serves OAuth 2.0
|
|
||||||
// protected resource metadata (RFC 9728) with CORS support.
|
|
||||||
//
|
|
||||||
// This handler allows cross-origin requests from any origin (Access-Control-Allow-Origin: *)
|
|
||||||
// because OAuth metadata is public information intended for client discovery (RFC 9728 §3.1).
|
|
||||||
// The metadata contains only non-sensitive configuration data about authorization servers
|
|
||||||
// and supported scopes.
|
|
||||||
//
|
|
||||||
// No validation of metadata fields is performed; ensure metadata accuracy at configuration time.
|
|
||||||
//
|
|
||||||
// For more sophisticated CORS policies or to restrict origins, wrap this handler with a
|
|
||||||
// CORS middleware like github.com/rs/cors or github.com/jub0bs/cors.
|
|
||||||
func ProtectedResourceMetadataHandler(metadata *oauthex.ProtectedResourceMetadata) http.Handler {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// Set CORS headers for cross-origin client discovery.
|
|
||||||
// OAuth metadata is public information, so allowing any origin is safe.
|
|
||||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
||||||
w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
|
|
||||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
|
||||||
|
|
||||||
// Handle CORS preflight requests
|
|
||||||
if r.Method == http.MethodOptions {
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only GET allowed for metadata retrieval
|
|
||||||
if r.Method != http.MethodGet {
|
|
||||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
if err := json.NewEncoder(w).Encode(metadata); err != nil {
|
|
||||||
http.Error(w, "Failed to encode metadata", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,222 +0,0 @@
|
||||||
// Copyright 2026 The Go MCP SDK Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by the license
|
|
||||||
// that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package auth_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"net/url"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/modelcontextprotocol/go-sdk/auth"
|
|
||||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
|
||||||
"github.com/modelcontextprotocol/go-sdk/oauthex"
|
|
||||||
"golang.org/x/oauth2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// savingTokenSource is an oauth2.TokenSource that passes the oauth2 config and
|
|
||||||
// token to the given saver function each time the access token value changes.
|
|
||||||
type savingTokenSource struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
src oauth2.TokenSource
|
|
||||||
saver func(*oauth2.Config, *oauth2.Token) error
|
|
||||||
config *oauth2.Config
|
|
||||||
accessToken string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *savingTokenSource) Token() (*oauth2.Token, error) {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
tok, err := s.src.Token()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if s.accessToken != tok.AccessToken {
|
|
||||||
s.accessToken = tok.AccessToken
|
|
||||||
// This saver implementation always returns nil.
|
|
||||||
_ = s.saver(s.config, tok)
|
|
||||||
}
|
|
||||||
return tok, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSavingTokenSource persists OAuth 2.0 sessions by intercepting token
|
|
||||||
// changes from the wrapped oauth2.TokenSource. When this wrapper detects an
|
|
||||||
// access token change, it calls the provided session saver with the
|
|
||||||
// oauth2.Config and the new oauth2.Token.
|
|
||||||
//
|
|
||||||
// initial is an optional access token the caller may already hold (such as a
|
|
||||||
// token loaded from storage). It initialises the wrapper's state so that if
|
|
||||||
// the first wrapped.Token() call returns the same token, does not trigger a
|
|
||||||
// redundant call to saver(). Pass nil when there is no existing token, in
|
|
||||||
// which case the first token produced by wrapped.Token() is saved.
|
|
||||||
func NewSavingTokenSource(wrapped oauth2.TokenSource, config *oauth2.Config, initial *oauth2.Token, saver func(*oauth2.Config, *oauth2.Token) error) oauth2.TokenSource {
|
|
||||||
if wrapped == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if saver == nil {
|
|
||||||
return wrapped
|
|
||||||
}
|
|
||||||
var accessToken string
|
|
||||||
if initial != nil {
|
|
||||||
accessToken = initial.AccessToken
|
|
||||||
}
|
|
||||||
return &savingTokenSource{
|
|
||||||
src: wrapped,
|
|
||||||
saver: saver,
|
|
||||||
config: config,
|
|
||||||
accessToken: accessToken,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sessionStore is an in-memory OAuth 2.0 session store used to demonstrate
|
|
||||||
// persistence. A production implementation would persist the session to disk or
|
|
||||||
// a secret store.
|
|
||||||
type sessionStore struct {
|
|
||||||
config *oauth2.Config
|
|
||||||
token *oauth2.Token
|
|
||||||
}
|
|
||||||
|
|
||||||
// save persists the given OAuth 2.0 config and token.
|
|
||||||
func (s *sessionStore) save(config *oauth2.Config, token *oauth2.Token) error {
|
|
||||||
fmt.Printf("Saving token: %s\n", token.AccessToken)
|
|
||||||
s.config = config
|
|
||||||
s.token = token
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// restore loads a previously persisted OAuth 2.0 session, if one exists.
|
|
||||||
func (s *sessionStore) restore() (*oauth2.Config, *oauth2.Token, error) {
|
|
||||||
if s.config != nil && s.token != nil {
|
|
||||||
fmt.Println("Restoring session.")
|
|
||||||
} else {
|
|
||||||
fmt.Println("No session found to restore.")
|
|
||||||
}
|
|
||||||
return s.config, s.token, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newMockAuthServer returns an httptest.Server that simulates both an MCP
|
|
||||||
// resource server requiring authorization and its OAuth authorization server.
|
|
||||||
func newMockAuthServer() *httptest.Server {
|
|
||||||
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
switch r.URL.Path {
|
|
||||||
case "/.well-known/oauth-authorization-server":
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
_, _ = fmt.Fprintf(w, `{"issuer": "http://%s", "authorization_endpoint": "http://%s/auth", "token_endpoint": "http://%s/token", "code_challenge_methods_supported": ["S256"]}`, r.Host, r.Host, r.Host)
|
|
||||||
case "/token":
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
_, _ = w.Write([]byte(`{"access_token": "mock-token", "token_type": "bearer"}`))
|
|
||||||
default:
|
|
||||||
// The mock MCP endpoint returns 401 until the client presents a valid
|
|
||||||
// bearer token.
|
|
||||||
if r.Header.Get("Authorization") == "Bearer mock-token" {
|
|
||||||
// A real server would return a valid MCP message here. The empty
|
|
||||||
// response causes Connect to return an error after authorization,
|
|
||||||
// which is ignored for this example which demonstrates token
|
|
||||||
// persistence only.
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.Header().Set("WWW-Authenticate", "Bearer")
|
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example shows how OAuth2 session persistence might be implemented. It
|
|
||||||
// connects twice using a shared in-memory session store: the first connection
|
|
||||||
// authorizes and saves the session, and the second restores and reuses it.
|
|
||||||
func Example_persistence() {
|
|
||||||
// Simulate an MCP server that requires authorization and an OAuth server.
|
|
||||||
mockServer := newMockAuthServer()
|
|
||||||
defer mockServer.Close()
|
|
||||||
|
|
||||||
// store persists the OAuth2 session across both connections.
|
|
||||||
store := &sessionStore{}
|
|
||||||
|
|
||||||
// connect performs a single client connection, restoring any saved session
|
|
||||||
// beforehand and saving any new session acquired during authorization.
|
|
||||||
connect := func() {
|
|
||||||
// Load the OAuth2 session if available, and use it.
|
|
||||||
var initialTS oauth2.TokenSource
|
|
||||||
cfg, tok, err := store.restore()
|
|
||||||
if err == nil && cfg != nil && tok != nil {
|
|
||||||
initialTS = NewSavingTokenSource(
|
|
||||||
cfg.TokenSource(context.Background(), tok),
|
|
||||||
cfg, tok, store.save,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure and initialize the AuthorizationCodeHandler with a
|
|
||||||
// NewTokenSource that saves the session when the access token changes,
|
|
||||||
// and the InitialTokenSource set to the session loaded via restore().
|
|
||||||
config := &auth.AuthorizationCodeHandlerConfig{
|
|
||||||
RedirectURL: "http://localhost/callback",
|
|
||||||
PreregisteredClient: &oauthex.ClientCredentials{
|
|
||||||
ClientID: "example",
|
|
||||||
},
|
|
||||||
Client: mockServer.Client(),
|
|
||||||
AuthorizationCodeFetcher: func(ctx context.Context, args *auth.AuthorizationArgs) (*auth.AuthorizationResult, error) {
|
|
||||||
fmt.Println("No token source found. Transport is calling Authorize()...")
|
|
||||||
// Extract the generated state from the authorization URL
|
|
||||||
u, _ := url.Parse(args.URL)
|
|
||||||
state := u.Query().Get("state")
|
|
||||||
return &auth.AuthorizationResult{Code: "mock-code", State: state}, nil
|
|
||||||
},
|
|
||||||
NewTokenSource: func(ctx context.Context, cfg *oauth2.Config, token *oauth2.Token) (oauth2.TokenSource, error) {
|
|
||||||
// This save implementation always returns nil.
|
|
||||||
_ = store.save(cfg, token)
|
|
||||||
return NewSavingTokenSource(
|
|
||||||
cfg.TokenSource(ctx, token), cfg, token, store.save,
|
|
||||||
), nil
|
|
||||||
},
|
|
||||||
InitialTokenSource: initialTS,
|
|
||||||
}
|
|
||||||
handler, err := auth.NewAuthorizationCodeHandler(config)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Error creating handler: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the constructed handler on a transport.
|
|
||||||
transport := &mcp.StreamableClientTransport{
|
|
||||||
Endpoint: mockServer.URL + "/sse",
|
|
||||||
OAuthHandler: handler,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a client and attempt to connect using the configured
|
|
||||||
// transport. The transport will automatically:
|
|
||||||
// 1. Call TokenSource() to check for an existing session. This will
|
|
||||||
// return InitialTokenSource, if set.
|
|
||||||
// 2. Try the MCP endpoint, and encounter a 401 response from the mock
|
|
||||||
// server.
|
|
||||||
// 3. Call Authorize() to perform the OAuth flow, which calls
|
|
||||||
// NewTokenSource. In this example NewTokenSource saves the newly
|
|
||||||
// acquired session.
|
|
||||||
// 4. Retry the MCP endpoint with a valid bearer token and get a 200.
|
|
||||||
client := mcp.NewClient(
|
|
||||||
&mcp.Implementation{Name: "example", Version: "1.0.0"}, nil,
|
|
||||||
)
|
|
||||||
// Response ignored: this example asserts authorization only.
|
|
||||||
_, _ = client.Connect(context.Background(), transport, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The first connection has no saved session, so it authorizes and saves.
|
|
||||||
fmt.Println("--- First connect ---")
|
|
||||||
connect()
|
|
||||||
// The second connection restores the saved session and reuses it, so no
|
|
||||||
// further authorization or save occurs.
|
|
||||||
fmt.Println("--- Second connect ---")
|
|
||||||
connect()
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// --- First connect ---
|
|
||||||
// No session found to restore.
|
|
||||||
// No token source found. Transport is calling Authorize()...
|
|
||||||
// Saving token: mock-token
|
|
||||||
// --- Second connect ---
|
|
||||||
// Restoring session.
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue