Skip to content

Commit 9f60242

Browse files
feat(pkgsite): adapt v1 API and redis rate limiting (#28)
1 parent 6a17872 commit 9f60242

25 files changed

Lines changed: 460 additions & 36 deletions

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# pkgsite-mcp
22

33
MCP tools for looking up current Go module and package information from the
4-
official `pkg.go.dev/v1beta` API.
4+
official `pkg.go.dev/v1` API.
55

66
Use it when you want a coding agent to answer Go dependency questions with
77
structured pkg.go.dev data instead of guessing from model memory, scraping HTML,
@@ -146,7 +146,7 @@ Health endpoint:
146146
http://localhost:8080/health
147147
```
148148

149-
Start optional Redis for local caching and rate limiting:
149+
Start Redis for local caching and distributed rate limiting:
150150

151151
```sh
152152
just up
@@ -179,7 +179,7 @@ http://localhost:8080/mcp
179179
## Configuration
180180

181181
```text
182-
PKGSITE_BASE_URL=https://pkg.go.dev/v1beta
182+
PKGSITE_BASE_URL=https://pkg.go.dev/v1
183183
KV_REDIS_URL=redis://localhost:9736/0
184184
KV_REDIS_POOL_SIZE=4
185185
KV_REDIS_MIN_IDLE_CONNS=2
@@ -205,8 +205,9 @@ O11Y_ENABLE_LOGS=true
205205
O11Y_ENABLE_METRICS=true
206206
```
207207

208-
Redis is optional. Without `KV_REDIS_URL`, requests go directly to pkg.go.dev and
209-
IP rate limiting is disabled. When Redis is configured, it backs both pkg.go.dev
210-
response caching and fixed-window IP rate limiting for `/mcp`.
208+
Redis backs response caching, the distributed outbound pkg.go.dev 45-QPS limiter,
209+
and fixed-window IP rate limiting for `/mcp`. Without `KV_REDIS_URL`, caching and
210+
both Redis-backed limiters are disabled; requests use direct upstream access
211+
without a process-local fallback limiter.
211212

212213
Sentry is optional. Without `SENTRY_DSN`, observability calls stay no-op.

fly.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ primary_region = 'iad'
88
[build]
99

1010
[env]
11-
PKGSITE_BASE_URL = 'https://pkg.go.dev/v1beta'
11+
PKGSITE_BASE_URL = 'https://pkg.go.dev/v1'
1212
PKGSITE_CACHE_DISABLED = 'false'
1313

1414
[http_service]

internal/config/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func read(getenv func(string) string) (Config, error) {
104104
EnableMetrics: p.boolean("O11Y_ENABLE_METRICS", true),
105105
},
106106
Pkgsite: Pkgsite{
107-
BaseURL: p.str("PKGSITE_BASE_URL", "https://pkg.go.dev/v1beta"),
107+
BaseURL: p.str("PKGSITE_BASE_URL", "https://pkg.go.dev/v1"),
108108
HTTPTimeout: p.duration("PKGSITE_HTTP_TIMEOUT", 10*time.Second),
109109
CacheDisabled: p.boolean("PKGSITE_CACHE_DISABLED", false),
110110
},

internal/config/config_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func TestReadDefaults(t *testing.T) {
2121
if got.Observability.FlushTimeout != 2*time.Second {
2222
t.Fatalf("FlushTimeout = %s, want 2s", got.Observability.FlushTimeout)
2323
}
24-
if got.Pkgsite.BaseURL != "https://pkg.go.dev/v1beta" {
24+
if got.Pkgsite.BaseURL != "https://pkg.go.dev/v1" {
2525
t.Fatalf("BaseURL = %q, want default", got.Pkgsite.BaseURL)
2626
}
2727
if got.Pkgsite.HTTPTimeout != 10*time.Second {

internal/mcpserver/skills/docs/operations.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,14 @@ Use `pkgsite_module` for module metadata, `pkgsite_versions` for available versi
55
Use `pkgsite_package` for package documentation metadata and `pkgsite_symbols` for exported API facts. `pkgsite_symbols` is usually the highest-signal tool for coding agents.
66

77
Use `pkgsite_vulns` before making security-sensitive recommendations. Use `pkgsite_imported_by` sparingly because the result set can be large.
8+
9+
List operations accept an upstream `filter` written as a Go expression that returns a boolean. The supported subset is:
10+
11+
- values `true`, `false`, and `nil`;
12+
- `==` and `!=` on any value;
13+
- `+`, `-`, `*`, `/`, and `%` on integers;
14+
- `+` on strings;
15+
- `<`, `<=`, `>`, and `>=` on strings and integers; and
16+
- parenthesized expressions.
17+
18+
The functions `contains(s, sub)`, `hasPrefix(s, pre)`, `hasSuffix(s, suf)`, and `matches(s, re)` are also available. `matches` takes a regular expression; a bare regular expression is not a valid filter. Each route exposes its JSON fields as variables, so examples include `name == "main"` for packages, `kind == "Type"` for symbols, and `hasPrefix(version, "v2.")` for versions. Pass the expression as plain text; the client percent-encodes query parameters.

internal/mcpserver/skills/docs/overview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# pkgsite-mcp overview
22

3-
Use pkgsite-mcp when you need current structured facts from `pkg.go.dev/v1beta` about Go modules, packages, versions, exported symbols, imported-by relationships, or vulnerabilities.
3+
Use pkgsite-mcp when you need current structured facts from `pkg.go.dev/v1` about Go modules, packages, versions, exported symbols, imported-by relationships, or vulnerabilities.
44

55
The source of truth is pkg.go.dev. This server is read-only and does not clone repositories, scrape HTML, or infer facts from training data.
66

internal/mcpserver/skills/docs/pagination.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@ There are two pagination layers.
44

55
Upstream pkg.go.dev pagination uses `limit` and `token`. When a response includes `upstreamNextPageToken`, pass it as `token` to fetch the next upstream page.
66

7+
Keep the rest of the upstream request unchanged when following a token; only add or replace `token`. A non-empty `upstreamNextPageToken` means another page exists even when the current page has no items.
8+
79
Local display pagination uses `start_at` and `max_tokens`. When metadata includes `next_start_at`, repeat the same tool call with that `start_at` to see the next local batch from the current upstream response.

internal/mcpserver/skills/docs/precision.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
Package paths can be ambiguous across modules. If a package lookup returns candidates or an ambiguity message, repeat the call with `module_path`.
44

5+
Error responses preserve the upstream `message`, `fixes`, and `candidates` fields. Prefer the suggested containing module from `candidates` or `fixes` instead of guessing which module owns an ambiguous package path.
6+
57
Use version-pinned calls when answering compatibility questions. Empty `version` means latest and can change.
68

79
Do not treat absence of a field as proof unless the raw upstream response makes that absence clear.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
List packages that import a package. Defaults to a conservative upstream limit because results can be large.
2+
3+
`filter` must be a Go expression that returns a boolean. It supports `true`, `false`, and `nil`; `==` and `!=` on any value; `+`, `-`, `*`, `/`, and `%` on integers; `+` on strings; `<`, `<=`, `>`, and `>=` on strings and integers; and parenthesized expressions. The functions `contains(s, sub)`, `hasPrefix(s, pre)`, `hasSuffix(s, suf)`, and `matches(s, re)` are available. `matches` takes a regular expression; a bare regular expression is not a valid filter. The route's JSON fields are available as variables; for imported-by results, use `path` to filter paths. Pass the expression as plain text; the client percent-encodes query parameters.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
List packages contained in a module version from pkg.go.dev.
2+
3+
`filter` must be a Go expression that returns a boolean. It supports `true`, `false`, and `nil`; `==` and `!=` on any value; `+`, `-`, `*`, `/`, and `%` on integers; `+` on strings; `<`, `<=`, `>`, and `>=` on strings and integers; and parenthesized expressions. The functions `contains(s, sub)`, `hasPrefix(s, pre)`, `hasSuffix(s, suf)`, and `matches(s, re)` are available. `matches` takes a regular expression; a bare regular expression is not a valid filter. JSON fields from each package are available as variables. Example: `name == "main"`. Pass the expression as plain text; the client percent-encodes query parameters.

0 commit comments

Comments
 (0)