Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,43 @@ jobs:
platforms: linux/amd64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

publish-mcp:
needs: build-and-push
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install MCP Publisher
run: |
curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" \
| tar xz mcp-publisher
chmod +x mcp-publisher

- name: Prepare server manifest
run: |
VERSION=${GITHUB_REF#refs/tags/v}
IMAGE_REPO=$(echo "${GITHUB_REPOSITORY}" | tr '[:upper:]' '[:lower:]')
IMAGE="ghcr.io/${IMAGE_REPO}:$VERSION"
jq --arg v "$VERSION" --arg image "$IMAGE" '
.version = $v
| .packages = (.packages // [] | map(
if .registryType == "oci" then
(.identifier = $image)
else .
end
| (if has("version") then .version = $v else . end)
))
' server.json > server.publish.json
mv server.publish.json server.json

- name: Login to MCP Registry
run: ./mcp-publisher login github-oidc

- name: Publish to MCP Registry
run: ./mcp-publisher publish
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@
- add an Alpine-based Docker image build + entrypoint script plus usage docs for container publishing
- publish the container automatically to GHCR using `.github/workflows/docker.yml`
- update Docker builder stage to the latest stable Rust toolchain (1.91.1) for smaller, faster binaries
- add a self-contained pkgx pantry (`pkgx/`) with build/test metadata so `pkgx cratedocs` can install the server via the pkgx runtime, plus README instructions for using and upstreaming it
- add `just install-pkgx` to verify the pkgx pantry wiring end-to-end (falls back to a helpful message until the package is mirrored onto dist.pkgx.dev)
63 changes: 62 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Rust Cargo Docs RAG MCP

`rust-cargo-docs-rag-mcp` is an MCP (Model Context Protocol) server that provides Rust crate documentation lookup and search tools intended for LLM assistants and other tooling.
`rust-cargo-docs-rag-mcp` is an MCP (Model Context Protocol) server that provides tools for Rust crate documentation lookup. It allows LLMs to look up documentation for Rust crates they are unfamiliar with.

This README focuses on how to build, version, release, and install the project using two common paths:
1. pkgx (build/install locally from source)
Expand All @@ -10,6 +10,30 @@ This README focuses on how to build, version, release, and install the project u

## Release / Versioning workflow (maintainers)

```bash
git clone https://github.com/promptexecution/rust-cargo-docs-rag-mcp.git
cd rust-cargo-docs-rag-mcp
cargo build --release
cargo install --path .
# Or install the pkgx-managed binary and check its version
just install-pkgx
```

### Installing with pkgx

The repository includes a mini [pkgx pantry](./pkgx) so you can build and run the CLI through `pkgx` without touching your global toolchain:

```bash
git clone https://github.com/promptexecution/rust-cargo-docs-rag-mcp.git
cd rust-cargo-docs-rag-mcp
export PKGX_PANTRY_PATH=$PWD/pkgx
export PKGX_PANTRY_DIR=$PWD/pkgx # pkgx^2 compatibility
pkgx cratedocs version
```

`pkgx` will download the tagged source tarball, compile `cratedocs` with the required Rust toolchain, and cache the result for subsequent runs. Once you're ready to upstream this package to the central [pkgx pantry](https://github.com/pkgxdev/pantry), copy `pkgx/projects/github.com/promptexecution/rust-cargo-docs-rag-mcp/package.yml` into a new PR there.

## Running the Server
This repository is wired to Cocogitto via `cog.toml`. Typical flow to create a release:

1. Install Cocogitto (once)
Expand Down Expand Up @@ -66,6 +90,32 @@ Run in stdio mode:
docker run --rm -e CRATEDOCS_MODE=stdio -i ghcr.io/promptexecution/rust-cargo-docs-rag-mcp:latest
```

### Using Docker

You can also build and run the server in an Alpine-based container. Prebuilt images are automatically published to GHCR via [`.github/workflows/docker.yml`](.github/workflows/docker.yml):

```bash
docker pull ghcr.io/promptexecution/rust-cargo-docs-rag-mcp:latest
```

To build locally (useful before pushing to another registry):

```bash
# Build the image (adjust the tag to match your registry)
docker build -t promptexecution/rust-cargo-docs-rag-mcp .

# Run HTTP/SSE mode on port 8080
docker run --rm -p 8080:8080 promptexecution/rust-cargo-docs-rag-mcp
```

Configuration is controlled through environment variables:
- `CRATEDOCS_MODE` (default `http`): switch to `stdio` to expose the stdio MCP server
- `CRATEDOCS_ADDRESS` (default `0.0.0.0:8080`): bind the HTTP server to a specific interface/port
- `CRATEDOCS_DEBUG` (default `false`): set to `true` to enable verbose logging in HTTP mode

All additional arguments appended to `docker run ... -- <args>` are forwarded to the underlying `cratedocs` process.

### Directly Testing Documentation Tools
### Environment Variables

- `CRATEDOCS_MODE` (default: `http`) — set to `stdio` to run the stdio MCP server
Expand Down Expand Up @@ -397,6 +447,17 @@ Then reference it normally in `mcp_settings.json`:

---

## Versioning & Releases

This repository includes a [`cog.toml`](./cog.toml) profile wired to [`scripts/set-version.sh`](./scripts/set-version.sh) so [Cocogitto](https://github.com/cocogitto/cocogitto) can bump the crate version and regenerate the changelog automatically.

Typical release flow:
1. `cargo install cocogitto` (once)
2. `cog bump minor` (or `patch`/`major`) – this updates `Cargo.toml`, `Cargo.lock`, and `CHANGELOG.md`
3. Review the generated changelog, run tests, and push the resulting tag/commit

See [`CHANGELOG.md`](./CHANGELOG.md) for the latest published versions.

## License

MIT License
Expand Down
12 changes: 10 additions & 2 deletions docker/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
#!/bin/sh
set -e
set -eu

# docker/entrypoint.sh - small wrapper to start cratedocs with the configured mode
MODE="${CRATEDOCS_MODE:-http}"
ADDRESS="${CRATEDOCS_ADDRESS:-0.0.0.0:8080}"
DEBUG="${CRATEDOCS_DEBUG:-false}"

if [ "$MODE" = "http" ]; then
if [ "$DEBUG" = "true" ]; then
exec /usr/local/bin/cratedocs http --address "$ADDRESS" --debug "$@"
else
exec /usr/local/bin/cratedocs http --address "$ADDRESS" "$@"
fi
else
exec /usr/local/bin/cratedocs "$MODE" "$@"
fi
# If explicit args provided, run with those
if [ "$#" -gt 0 ]; then
exec /usr/local/bin/cratedocs "$@"
Expand Down
7 changes: 7 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ pkgx-test:
run:
cargo run --bin cratedocs http --address 0.0.0.0:3000 --debug

install-pkgx:
@echo "Using pkgx pantry at {{invocation_directory()}}/pkgx"
PKGX_PANTRY_PATH={{invocation_directory()}}/pkgx \
PKGX_PANTRY_DIR={{invocation_directory()}}/pkgx \
pkgx cratedocs version || \
(echo "pkgx failed (likely no network); see README for manual steps" && exit 1)

docker-build:
docker build -t promptexecution/rust-cargo-docs-rag-mcp .

Expand Down
6 changes: 6 additions & 0 deletions pkgx/pkgx.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
dependencies:
pkgx.sh/brewkit: ^0 || ^1

env:
PKGX_PANTRY_PATH: ${{srcroot}}
PKGX_PANTRY_DIR: ${{srcroot}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
distributable:
url: https://github.com/promptexecution/rust-cargo-docs-rag-mcp/archive/refs/tags/{{ version.tag }}.tar.gz
strip-components: 1

versions:
github: promptexecution/rust-cargo-docs-rag-mcp

dependencies:
openssl.org: '>=1.1'

build:
dependencies:
rust-lang.org: '>=1.91'
rust-lang.org/cargo: '*'
script:
- cargo install --locked \
--root={{ prefix }} \
--path=. \
--bin cratedocs

provides:
- bin/cratedocs

test:
- cratedocs version | grep {{version}}
64 changes: 47 additions & 17 deletions scripts/set-version.sh
Original file line number Diff line number Diff line change
@@ -1,22 +1,52 @@
#!/bin/sh
set -e
#!/usr/bin/env bash
set -euo pipefail

if [ -z "$1" ]; then
echo "Usage: $0 <version>" >&2
exit 2
if [ "$#" -ne 1 ]; then
echo "usage: $0 <semver>" >&2
exit 1
fi
VER="$1"

# Update Cargo.toml version field
if command -v perl >/dev/null 2>&1; then
perl -0777 -pe "s/^version\s*=\s*\".*\"/version = \"${VER}\"/m" -i Cargo.toml
else
sed -E "s/^version[[:space:]]*=.*$/version = \"${VER}\"/" Cargo.toml > Cargo.toml.tmp && mv Cargo.toml.tmp Cargo.toml
fi
version="$1"

# Regenerate lockfile
if command -v cargo >/dev/null 2>&1; then
cargo generate-lockfile || true
fi
# Update Cargo.toml package version (first occurrence only to avoid dependency matches)
python3 - "$version" <<'PY'
import pathlib, re, sys
version = sys.argv[1]
path = pathlib.Path("Cargo.toml")
text = path.read_text()
new_text, count = re.subn(r'(?m)^(version\s*=\s*)"[^"]+"', rf'\1"{version}"', text, count=1)
if count != 1:
raise SystemExit("Could not update version in Cargo.toml")
path.write_text(new_text)
PY

# Update Cargo.lock entry for this crate
python3 - "$version" <<'PY'
import pathlib, sys
version = sys.argv[1]
path = pathlib.Path("Cargo.lock")
lines = path.read_text().splitlines()
out_lines = []
in_pkg = False
target = 'name = "rust-cargo-docs-rag-mcp"'
updated = False
for line in lines:
stripped = line.strip()
if stripped == target:
in_pkg = True
out_lines.append(line)
continue
if in_pkg and stripped.startswith("version = "):
out_lines.append(f'version = "{version}"')
in_pkg = False
updated = True
continue
if stripped.startswith("name = ") and stripped != target:
in_pkg = False
out_lines.append(line)

if not updated:
raise SystemExit("Could not update version in Cargo.lock")

exit 0
path.write_text("\n".join(out_lines) + "\n")
PY
29 changes: 29 additions & 0 deletions server.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
"name": "io.github.promptexecution/rust-cargo-docs-rag-mcp",
"title": "Rust Cargo Docs RAG",
"description": "Lookup Rust crate and item documentation via docs.rs and crates.io search.",
"websiteUrl": "https://github.com/promptexecution/rust-cargo-docs-rag-mcp",
"repository": {
"url": "https://github.com/promptexecution/rust-cargo-docs-rag-mcp",
"source": "github"
},
"version": "0.3.0",
"packages": [
{
"registryType": "oci",
"identifier": "ghcr.io/promptexecution/rust-cargo-docs-rag-mcp:0.3.0",
"runtimeHint": "docker",
"transport": {
"type": "stdio"
},
"environmentVariables": [
{
"name": "CRATEDOCS_MODE",
"value": "stdio",
"description": "Ensure the container exposes the MCP stdio transport when run by clients."
}
]
}
]
}