Skip to content

Commit e11783e

Browse files
committed
init commit
0 parents  commit e11783e

File tree

18 files changed

+1953
-0
lines changed

18 files changed

+1953
-0
lines changed

.github/copilot-instructions.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Copilot Instructions for `hypermcp`
2+
3+
These instructions help AI coding agents work productively in this repository. They describe the architecture, key workflows, and project-specific patterns you should follow and extend.
4+
5+
## Big Picture
6+
7+
`hypermcp` is a small, reusable Go library that provides common infrastructure for building Model Context Protocol (MCP) servers. It wraps the MCP Go SDK with:
8+
- A configured HTTP client (`httpx.Client`) with retries, timeouts, and limits
9+
- A simple caching layer (`cache.Cache`) built on Ristretto
10+
- Structured logging via `zap`
11+
- Transport helpers for stdio (implemented) and Streamable HTTP (placeholder)
12+
13+
Core types:
14+
- `Server` (`server.go`): owns `mcp.Server`, `httpx.Client`, `cache.Cache`, and `zap.Logger`; exposes helpers and registration stats.
15+
- `TransportType` + `RunWithTransport` (`transport.go`): selects and starts transport (only `stdio` implemented).
16+
- `ServerInfo` (`version.go`): simple version struct and formatter.
17+
18+
Typical usage (from `README.md`): create a `Server` with `Config`, build providers using `srv.HTTPClient()/Cache()/Logger()`, register tools/resources, log counts, then run with stdio.
19+
20+
## Project Conventions
21+
22+
- Package name: `hypermcp`; import path: `github.com/rayprogramming/hypermcp`.
23+
- Prefer dependency injection via the `Server` getters over global singletons.
24+
- Register MCP features via `srv.AddTool`, `srv.AddResource`, `srv.AddResourceTemplate` on `srv.MCP()` or helpers on `Server` (see README examples). After each registration, call `srv.IncrementToolCount()` or `srv.IncrementResourceCount()` so `LogRegistrationStats()` is accurate.
25+
- Use `RunWithTransport(ctx, srv, TransportStdio, logger)` for starting servers; HTTP transport is not implemented and should return a clear error.
26+
- Logging: use the `srv.Logger()`; keep logs structured and low-noise in hot paths. Cache/HTTP helpers already log at `Debug`/`Warn` where appropriate.
27+
28+
## httpx Client Patterns (`httpx/httpx.go`)
29+
30+
- Use `Client.Get(ctx, url, &result)` for JSON GET requests; it sets headers and delegates to `DoJSON`.
31+
- `DoJSON` handles retries (3 max) for 429/5xx, request timeout (~6s), and caps response size at 10MB. It returns permanent errors for non-retryable statuses and JSON decode errors.
32+
- Always pass `context.Context`; cancellation/timeouts are respected and covered by tests.
33+
- Example:
34+
```go
35+
type Payload struct { Message string `json:"message"` }
36+
var out Payload
37+
if err := srv.HTTPClient().Get(ctx, "https://api.example.com/status", &out); err != nil { /* handle */ }
38+
```
39+
40+
## Cache Patterns (`cache/cache.go`)
41+
42+
- `cache.Cache` stores arbitrary values with TTL tracking (internal map) on top of Ristretto. Methods: `Get`, `Set(key, value, ttl)`, `Delete`, `Clear`, `Metrics`, `Close`.
43+
- TTL is enforced via a background cleanup goroutine and checked on `Get`. If expired, the key is deleted lazily.
44+
- Cost is roughly estimated (constant 64). If you store large payloads, prefer storing pointers or compact structs to avoid exceeding `MaxCost`.
45+
- Example:
46+
```go
47+
v, ok := srv.Cache().Get(cacheKey)
48+
if ok { /* use v */ }
49+
// fill and set for 1 minute
50+
srv.Cache().Set(cacheKey, data, time.Minute)
51+
```
52+
53+
## Transport (`transport.go`)
54+
55+
- Use `TransportStdio` for most MCP servers. Starting with Streamable HTTP should return `fmt.Errorf("Streamable HTTP transport not yet implemented")` — don’t attempt to wire HTTP until implemented here.
56+
- `RunWithTransport` logs selected transport, sets up `mcp.StdioTransport`, then calls `srv.Run(ctx, transport)`.
57+
58+
## Tests & Benchmarks
59+
60+
- Unit tests exist for `cache` and `httpx` packages:
61+
- `cache/cache_test.go`: Get/Set, expiration, delete, and basic benchmarks.
62+
- `httpx/httpx_test.go`: success path, retry behavior, context cancellation, and benchmark.
63+
- Run with:
64+
- All: `go test ./...`
65+
- Specific pkg: `go test ./cache -v`
66+
- Benchmarks: `go test -bench=. ./cache`
67+
68+
## Developer Workflows
69+
70+
- Build: standard `go build ./...` (no custom build tags). This library is imported by downstream MCP servers.
71+
- Logging setup: use `zap.NewProduction()` in binaries; tests use `zaptest`.
72+
- Versioning: `ServerInfo` is available for embedding version/build info in your binaries.
73+
- Error handling: return wrapped errors (`fmt.Errorf("context", %w)`) from helpers; transport selection returns explicit errors for unsupported modes.
74+
75+
## Integration Points
76+
77+
- MCP SDK: `github.com/modelcontextprotocol/go-sdk/mcp` — use `srv.MCP()` if you need direct access to register tools/resources beyond the helpers shown in README.
78+
- HTTP deps: `cenkalti/backoff/v4` for retries; standard `net/http`; headers pre-set in `Get`.
79+
- Cache dep: `github.com/dgraph-io/ristretto` with metrics available via `Cache.Metrics()`.
80+
81+
## Gotchas
82+
83+
- Cache is created even when `CacheEnabled=false` (as a minimal instance). Don’t assume `srv.Cache()` is nil.
84+
- Response bodies over 10MB will be truncated by `httpx` and cause errors. Choose APIs accordingly or stream/process incrementally.
85+
- Streamable HTTP transport is intentionally unimplemented — calling it should be treated as a configuration error.

.github/workflows/ci.yml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
build-and-test:
14+
name: Lint, Test, and Build
15+
runs-on: ubuntu-latest
16+
timeout-minutes: 15
17+
env:
18+
GO_VERSION: '1.22.x'
19+
steps:
20+
- name: Checkout
21+
uses: actions/checkout@v4
22+
23+
- name: Setup Go
24+
uses: actions/setup-go@v5
25+
with:
26+
go-version: ${{ env.GO_VERSION }}
27+
check-latest: true
28+
cache: true
29+
30+
- name: Cache Go build
31+
uses: actions/cache@v4
32+
with:
33+
path: |
34+
~/.cache/go-build
35+
${{ github.workspace }}/pkg/mod
36+
key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }}
37+
restore-keys: |
38+
${{ runner.os }}-go-${{ env.GO_VERSION }}-
39+
${{ runner.os }}-go-
40+
41+
- name: Verify modules
42+
run: |
43+
go env
44+
go mod download
45+
go mod verify
46+
47+
- name: Lint (go vet)
48+
run: |
49+
go vet ./...
50+
51+
- name: Lint (golangci-lint)
52+
uses: golangci/golangci-lint-action@v6
53+
with:
54+
version: v1.60.3
55+
args: --timeout=3m
56+
57+
- name: Run tests
58+
run: |
59+
go test ./... -race -coverprofile=coverage.out -covermode=atomic
60+
61+
- name: Upload coverage report
62+
if: always()
63+
uses: actions/upload-artifact@v4
64+
with:
65+
name: coverage-report
66+
path: coverage.out
67+
68+
- name: Build library (compile check)
69+
run: |
70+
go build ./...

.github/workflows/release.yml

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
validate:
13+
name: Lint and Test
14+
runs-on: ubuntu-latest
15+
timeout-minutes: 15
16+
env:
17+
GO_VERSION: '1.22.x'
18+
steps:
19+
- name: Checkout
20+
uses: actions/checkout@v4
21+
- name: Setup Go
22+
uses: actions/setup-go@v5
23+
with:
24+
go-version: ${{ env.GO_VERSION }}
25+
check-latest: true
26+
cache: true
27+
- name: Verify modules
28+
run: |
29+
go mod download
30+
go mod verify
31+
- name: Lint (go vet)
32+
run: |
33+
go vet ./...
34+
- name: Lint (golangci-lint)
35+
uses: golangci/golangci-lint-action@v6
36+
with:
37+
version: v1.60.3
38+
args: --timeout=3m
39+
- name: Run tests
40+
run: |
41+
go test ./... -race -coverprofile=coverage.out -covermode=atomic
42+
- name: Upload coverage report
43+
if: always()
44+
uses: actions/upload-artifact@v4
45+
with:
46+
name: coverage-report
47+
path: coverage.out
48+
49+
build:
50+
name: Build artifacts
51+
runs-on: ubuntu-latest
52+
needs: validate
53+
env:
54+
GO_VERSION: '1.22.x'
55+
steps:
56+
- name: Checkout
57+
uses: actions/checkout@v4
58+
- name: Setup Go
59+
uses: actions/setup-go@v5
60+
with:
61+
go-version: ${{ env.GO_VERSION }}
62+
check-latest: true
63+
cache: true
64+
- name: Build (Linux amd64)
65+
env:
66+
GOOS: linux
67+
GOARCH: amd64
68+
run: |
69+
go build -v ./...
70+
- name: Archive source
71+
run: |
72+
git archive --format tar.gz -o source.tar.gz HEAD
73+
- name: Upload artifacts
74+
uses: actions/upload-artifact@v4
75+
with:
76+
name: build-artifacts
77+
path: |
78+
source.tar.gz
79+
80+
release:
81+
name: Create GitHub Release
82+
runs-on: ubuntu-latest
83+
needs: build
84+
steps:
85+
- name: Download artifacts
86+
uses: actions/download-artifact@v4
87+
with:
88+
name: build-artifacts
89+
path: .
90+
- name: Create Release
91+
uses: softprops/action-gh-release@v2
92+
with:
93+
files: |
94+
source.tar.gz
95+
generate_release_notes: true
96+
env:
97+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Binaries
2+
*.exe
3+
*.exe~
4+
*.dll
5+
*.so
6+
*.dylib
7+
bin/
8+
dist/
9+
10+
# Test binary
11+
*.test
12+
13+
# Output of go coverage tool
14+
*.out
15+
coverage.txt
16+
coverage.html
17+
18+
# Go workspace file
19+
go.work
20+
21+
# IDE
22+
.vscode/
23+
.idea/
24+
*.swp
25+
*.swo
26+
*~
27+
28+
# OS
29+
.DS_Store
30+
Thumbs.db

0 commit comments

Comments
 (0)