diff --git a/.github/prompts/agentic-merge-upstream.prompt.md b/.github/prompts/agentic-merge-upstream.prompt.md new file mode 100644 index 000000000..04e36503c --- /dev/null +++ b/.github/prompts/agentic-merge-upstream.prompt.md @@ -0,0 +1,229 @@ +# Merge Upstream SDK Changes + +You are an expert Java developer tasked with porting changes from the official Copilot SDK (primarily the .NET implementation) to this Java SDK. + +## ⚠️ IMPORTANT: Java SDK Design Takes Priority + +**The current design and architecture of the Java SDK is the priority.** When porting changes from upstream: + +1. **Adapt, don't copy** - Translate upstream features to fit the Java SDK's existing patterns, naming conventions, and architecture +2. **Preserve Java idioms** - The Java SDK should feel natural to Java developers, not like a C# port +3. **Maintain consistency** - New code must match the existing codebase style and structure +4. **Evaluate before porting** - Not every upstream change needs to be ported; some may not be applicable or may conflict with Java SDK design decisions + +Before making any changes, **read and understand the existing Java SDK implementation** to ensure new code integrates seamlessly. + +## Workflow Overview + +1. Clone upstream repository +2. Analyze diff since last merge +3. Apply changes to Java SDK +4. Test and fix issues +5. Update documentation +6. Leave changes uncommitted for review + +--- + +## Step 1: Clone Upstream Repository + +Clone the official Copilot SDK repository into a temporary folder: + +```bash +UPSTREAM_REPO="https://github.com/github/copilot-sdk.git" +TEMP_DIR=$(mktemp -d) +git clone --depth=100 "$UPSTREAM_REPO" "$TEMP_DIR/copilot-sdk" +``` + +## Step 2: Read Last Merge Commit + +Read the commit hash from `.lastmerge` file in the Java SDK root: + +```bash +LAST_MERGE_COMMIT=$(cat .lastmerge) +echo "Last merged commit: $LAST_MERGE_COMMIT" +``` + +## Step 3: Analyze Changes + +Generate a diff between the last merged commit and HEAD of main: + +```bash +cd "$TEMP_DIR/copilot-sdk" +git fetch origin main +git log --oneline "$LAST_MERGE_COMMIT"..origin/main +git diff "$LAST_MERGE_COMMIT"..origin/main --stat +``` + +Focus on analyzing: +- `dotnet/src/` - Primary reference implementation +- `dotnet/test/` - Test cases to port +- `docs/` - Documentation updates +- `sdk-protocol-version.json` - Protocol version changes + +## Step 4: Identify Changes to Port + +For each change in the upstream diff, determine: + +1. **New Features**: New methods, classes, or capabilities added to the SDK +2. **Bug Fixes**: Corrections to existing functionality +3. **API Changes**: Changes to public interfaces or method signatures +4. **Protocol Updates**: Changes to the JSON-RPC protocol or message types +5. **Test Updates**: New or modified test cases + +### Key Files to Compare + +| Upstream (.NET) | Java SDK Equivalent | +|------------------------------------|--------------------------------------------------------| +| `dotnet/src/Client.cs` | `src/main/java/com/github/copilot/sdk/CopilotClient.java` | +| `dotnet/src/Session.cs` | `src/main/java/com/github/copilot/sdk/CopilotSession.java` | +| `dotnet/src/Types.cs` | `src/main/java/com/github/copilot/sdk/types/*.java` | +| `dotnet/src/Generated/*.cs` | `src/main/java/com/github/copilot/sdk/types/*.java` | +| `dotnet/test/*.cs` | `src/test/java/com/github/copilot/sdk/*Test.java` | +| `docs/getting-started.md` | `README.md` and `src/site/markdown/*.md` | +| `docs/*.md` (new files) | `src/site/markdown/*.md` + update `src/site/site.xml` | +| `sdk-protocol-version.json` | (embedded in Java code or resource file) | + +> **⚠️ Important:** When adding new documentation pages, always update `src/site/site.xml` to include them in the navigation menu. + +## Step 5: Apply Changes to Java SDK + +When porting changes: + +### ⚠️ Priority: Preserve Java SDK Design + +Before modifying any code: + +1. **Read the existing Java implementation first** - Understand current patterns, class structure, and naming +2. **Identify the Java equivalent approach** - Don't replicate C# patterns; find the idiomatic Java way +3. **Check for existing abstractions** - The Java SDK may already have mechanisms that differ from .NET +4. **Preserve backward compatibility** - Existing API signatures should not break unless absolutely necessary +5. **When in doubt, match existing code** - Follow what's already in the Java SDK, not the upstream + +### General Guidelines + +- **Naming Conventions**: Convert C# PascalCase to Java camelCase for methods/variables +- **Async Patterns**: C# `async/await` → Java `CompletableFuture` or synchronous equivalents +- **Nullable Types**: C# `?` nullable → Java `@Nullable` annotations or `Optional` +- **Properties**: C# properties → Java getters/setters or records +- **Records**: C# records → Java records (Java 17+) +- **Events**: C# events → Java callbacks or listeners + +### Type Mappings + +| C# Type | Java Equivalent | +|------------------------|------------------------------| +| `string` | `String` | +| `int` | `int` / `Integer` | +| `bool` | `boolean` / `Boolean` | +| `Task` | `CompletableFuture` | +| `CancellationToken` | (custom implementation) | +| `IAsyncEnumerable` | `Stream` or `Iterator` | +| `JsonElement` | `JsonNode` (Jackson) | +| `Dictionary` | `Map` | +| `List` | `List` | + +### Code Style + +Follow the existing Java SDK patterns: +- Use Jackson for JSON serialization (`ObjectMapper`) +- Use Java records for DTOs where appropriate +- Follow the existing package structure under `com.github.copilot.sdk` +- Maintain backward compatibility when possible +- **Match the style of surrounding code** - Consistency with existing code is more important than upstream patterns +- **Prefer existing abstractions** - If the Java SDK already solves a problem differently than .NET, keep the Java approach + +## Step 6: Format and Run Tests + +After applying changes, format the code and run the test suite: + +```bash +mvn spotless:apply +mvn clean test +``` + +**Important:** Always run `mvn spotless:apply` before testing to ensure code formatting is consistent with project standards. + +### If Tests Fail + +1. Read the test output carefully +2. Identify the root cause (compilation error, runtime error, assertion failure) +3. Fix the issue in the Java code +4. Re-run tests +5. Repeat until all tests pass + +### Common Issues + +- **Missing imports**: Add required import statements +- **Type mismatches**: Ensure proper type conversions +- **Null handling**: Add null checks where C# had nullable types +- **JSON serialization**: Verify Jackson annotations are correct + +## Step 7: Build the Package + +Once tests pass, build the complete package: + +```bash +mvn clean package -DskipTests +``` + +Verify: +- No compilation errors +- No warnings (if possible) +- JAR file is generated in `target/` + +## Step 8: Update Documentation + +Review and update documentation as needed: + +1. **README.md**: Update if there are new features or API changes +2. **src/site/markdown/documentation.md**: Update detailed documentation +3. **Javadoc**: Add/update Javadoc comments for new/changed public APIs +4. **CHANGELOG**: (if exists) Add entry for the changes + +## Step 9: Update Last Merge Reference + +Update the `.lastmerge` file with the new HEAD commit: + +```bash +cd "$TEMP_DIR/copilot-sdk" +NEW_COMMIT=$(git rev-parse origin/main) +echo "$NEW_COMMIT" > /.lastmerge +``` + +## Step 10: Final Review (DO NOT COMMIT) + +Before finishing: + +1. Run `git status` to see all changed files +2. Run `git diff` to review all changes +3. Ensure no unintended changes were made +4. Verify code follows project conventions +5. **DO NOT COMMIT** - leave changes staged/unstaged for user review + +--- + +## Checklist + +- [ ] Upstream repository cloned +- [ ] Diff analyzed between `.lastmerge` commit and HEAD +- [ ] New features/fixes identified +- [ ] Changes ported to Java SDK following conventions +- [ ] `mvn test` passes +- [ ] `mvn package` builds successfully +- [ ] Documentation updated +- [ ] `src/site/site.xml` updated if new documentation pages were added +- [ ] `.lastmerge` file updated with new commit hash +- [ ] Changes left uncommitted for review + +--- + +## Notes + +- The upstream SDK is at: `https://github.com/github/copilot-sdk.git` +- Primary reference implementation is in `dotnet/` folder +- This Java SDK targets Java 17+ +- Uses Jackson for JSON processing +- Uses JUnit 5 for testing +- **Java SDK design decisions take precedence over upstream patterns** +- **Adapt upstream changes to fit Java idioms, not the other way around** + diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 000000000..0146a4a5a --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,31 @@ +# Configuration for automatically generated release notes +# https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes + +changelog: + exclude: + labels: + - ignore-for-release + authors: + - dependabot + - github-actions[bot] + categories: + - title: 🚀 Features + labels: + - enhancement + - feature + - title: 🐛 Bug Fixes + labels: + - bug + - fix + - title: 📚 Documentation + labels: + - documentation + - docs + - title: 🔧 Maintenance + labels: + - chore + - maintenance + - dependencies + - title: 📦 Other Changes + labels: + - "*" diff --git a/.github/templates/versions.html b/.github/templates/versions.html new file mode 100644 index 000000000..8281a9d96 --- /dev/null +++ b/.github/templates/versions.html @@ -0,0 +1,126 @@ + + + + Documentation Versions - Copilot SDK for Java + + + +
+

📚 Copilot SDK for Java

+

Documentation Versions

+ +
+ ⚠️ Disclaimer: This is an unofficial, community-driven SDK and is not supported or endorsed by GitHub. Use at your own risk. +
+ +
+ 💡 Tip: We recommend using the Latest Release documentation unless you need features from an unreleased version. +
+ +

Available Versions

+
    + +
+ + +
+ + diff --git a/.github/workflows/sdk-build.yml b/.github/workflows/build-test.yml similarity index 98% rename from .github/workflows/sdk-build.yml rename to .github/workflows/build-test.yml index 5e66b4fc2..5381c7816 100644 --- a/.github/workflows/sdk-build.yml +++ b/.github/workflows/build-test.yml @@ -1,4 +1,4 @@ -name: "SDK E2E Tests" +name: "Build & Test" env: COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} diff --git a/.github/workflows/deploy-site.yml b/.github/workflows/deploy-site.yml new file mode 100644 index 000000000..d5b8ebf90 --- /dev/null +++ b/.github/workflows/deploy-site.yml @@ -0,0 +1,256 @@ +# Workflow for deploying versioned documentation to GitHub Pages +name: Deploy Documentation + +on: + # Runs on pushes targeting the default branch (publishes to /snapshot/) + push: + branches: ["main"] + paths: + - 'src/site/**' + - 'src/main/java/**' + - 'pom.xml' + - '.github/workflows/site.yml' + + # Runs on release publish (publishes to /latest/ and /vX.Y.Z/) + release: + types: [published] + + # Allows manual trigger + workflow_dispatch: + inputs: + version: + description: 'Specific version/tag to build (leave empty for snapshot from main)' + type: string + default: '' + publish_as_latest: + description: 'Also publish as latest?' + type: boolean + default: false + rebuild_all_versions: + description: 'Rebuild documentation for all release tags?' + type: boolean + default: false + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: write + pages: write + id-token: write + +# Allow only one concurrent deployment +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Set up JDK 17 + uses: actions/setup-java@v5 + with: + java-version: '17' + distribution: 'temurin' + + - name: Checkout gh-pages branch + uses: actions/checkout@v6 + with: + ref: gh-pages + path: site + continue-on-error: true + + - name: Initialize site if needed + run: | + if [ ! -d "site/.git" ]; then + rm -rf site + mkdir -p site + cd site + git init + git checkout -b gh-pages + git remote add origin "https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git" + fi + + - name: Get all release tags + id: tags + run: | + # Get all tags that look like version numbers (v1.0.0 or 1.0.0) + TAGS=$(git tag -l | grep -E '^v?[0-9]+\.[0-9]+' | sort -Vr) + echo "all_tags<> $GITHUB_OUTPUT + echo "$TAGS" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + # Get the latest tag + LATEST_TAG=$(echo "$TAGS" | head -n 1) + echo "latest_tag=$LATEST_TAG" >> $GITHUB_OUTPUT + + # Determine what to build + if [[ "${{ github.event_name }}" == "release" ]]; then + echo "build_tag=${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT + echo "is_release=true" >> $GITHUB_OUTPUT + elif [[ -n "${{ inputs.version }}" ]]; then + # Manual trigger with specific version + VERSION="${{ inputs.version }}" + # Add 'v' prefix if not present for tag lookup + if [[ ! "$VERSION" =~ ^v ]]; then + TAG="v$VERSION" + else + TAG="$VERSION" + fi + echo "build_tag=$TAG" >> $GITHUB_OUTPUT + echo "is_release=true" >> $GITHUB_OUTPUT + else + echo "build_tag=" >> $GITHUB_OUTPUT + echo "is_release=false" >> $GITHUB_OUTPUT + fi + + - name: Build snapshot documentation (main branch) + if: steps.tags.outputs.is_release == 'false' && inputs.rebuild_all_versions != true + run: | + ./mvnw clean site -DskipTests -Dcheckstyle.skip=true + + rm -rf "site/snapshot" + mkdir -p "site/snapshot" + cp -r target/site/* "site/snapshot/" + + - name: Build documentation for specific version + if: steps.tags.outputs.is_release == 'true' && inputs.rebuild_all_versions != true + run: | + TAG="${{ steps.tags.outputs.build_tag }}" + VERSION="${TAG#v}" # Remove 'v' prefix + + echo "Building documentation for $TAG (version $VERSION)" + git checkout "$TAG" + + ./mvnw clean site -DskipTests -Dcheckstyle.skip=true + + rm -rf "site/$VERSION" + mkdir -p "site/$VERSION" + cp -r target/site/* "site/$VERSION/" + + # Update /latest/ if this is a release event or publish_as_latest is true + if [[ "${{ github.event_name }}" == "release" ]] || [[ "${{ inputs.publish_as_latest }}" == "true" ]]; then + rm -rf "site/latest" + mkdir -p "site/latest" + cp -r target/site/* "site/latest/" + fi + + git checkout main + + - name: Build documentation for all release tags + if: inputs.rebuild_all_versions == true + run: | + LATEST_TAG="${{ steps.tags.outputs.latest_tag }}" + + while IFS= read -r TAG; do + if [[ -z "$TAG" ]]; then continue; fi + + VERSION="${TAG#v}" # Remove 'v' prefix + echo "Building documentation for $TAG (version $VERSION)" + + git checkout "$TAG" + ./mvnw clean site -DskipTests -Dcheckstyle.skip=true + + rm -rf "site/$VERSION" + mkdir -p "site/$VERSION" + cp -r target/site/* "site/$VERSION/" + + # If this is the latest tag, also update /latest/ + if [[ "$TAG" == "$LATEST_TAG" ]]; then + rm -rf "site/latest" + mkdir -p "site/latest" + cp -r target/site/* "site/latest/" + fi + + done <<< "${{ steps.tags.outputs.all_tags }}" + + # Return to main and build snapshot + git checkout main + ./mvnw clean site -DskipTests -Dcheckstyle.skip=true + + rm -rf "site/snapshot" + mkdir -p "site/snapshot" + cp -r target/site/* "site/snapshot/" + + - name: Copy version index page + run: | + cp .github/templates/versions.html site/index.html + + - name: Update version list from git tags + run: | + cd site + + # Get versions from git tags (already sorted by version, descending) + VERSIONS=$(git -C .. tag -l | grep -E '^v?[0-9]+\.[0-9]+' | sed 's/^v//' | sort -Vr) + HAS_SNAPSHOT=$([ -d "snapshot" ] && echo "true" || echo "false") + HAS_LATEST=$([ -d "latest" ] && echo "true" || echo "false") + + # Generate version links + VERSION_HTML="" + + # Add latest link if exists + if [ "$HAS_LATEST" = "true" ]; then + VERSION_HTML+='
  • Latest Releaselatest
  • ' + fi + + # Add snapshot if exists + if [ "$HAS_SNAPSHOT" = "true" ]; then + VERSION_HTML+='
  • Development (main branch)snapshot
  • ' + fi + + # Add versioned releases from tags + for v in $VERSIONS; do + if [ -d "$v" ]; then + VERSION_HTML+="
  • Version $v$v
  • " + fi + done + + # Update the index.html using the placeholder + sed -i "s||$VERSION_HTML|" index.html + + - name: Deploy to GitHub Pages + run: | + cd site + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + # Set remote URL with token for authentication + git remote set-url origin "https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git" 2>/dev/null || \ + git remote add origin "https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git" + + git add -A + + # Create descriptive commit message + if [[ "${{ inputs.rebuild_all_versions }}" == "true" ]]; then + COMMIT_MSG="Rebuild documentation for all versions" + elif [[ "${{ steps.tags.outputs.is_release }}" == "true" ]]; then + COMMIT_MSG="Deploy documentation: ${{ steps.tags.outputs.build_tag }}" + else + COMMIT_MSG="Deploy documentation: snapshot" + fi + + git diff --staged --quiet || git commit -m "$COMMIT_MSG" + + # Push, creating the branch if it doesn't exist + git push -u origin gh-pages --force + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Pages + uses: actions/configure-pages@v5 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v4 + with: + path: 'site' + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/notes.template b/.github/workflows/notes.template new file mode 100644 index 000000000..40c6f29e5 --- /dev/null +++ b/.github/workflows/notes.template @@ -0,0 +1,24 @@ +# Installation + +> ⚠️ **Disclaimer:** This is an **unofficial, community-driven SDK** and is **not supported or endorsed by GitHub**. Use at your own risk. + +📦 [View on Maven Central](https://central.sonatype.com/artifact/${GROUP_ID}/${ARTIFACT_ID}/${VERSION}) + +## Maven +```xml + + ${GROUP_ID} + ${ARTIFACT_ID} + ${VERSION} + +``` + +## Gradle (Kotlin DSL) +```kotlin +implementation("${GROUP_ID}:${ARTIFACT_ID}:${VERSION}") +``` + +## Gradle (Groovy DSL) +```groovy +implementation '${GROUP_ID}:${ARTIFACT_ID}:${VERSION}' +``` \ No newline at end of file diff --git a/.github/workflows/publish-maven.yml b/.github/workflows/publish-maven.yml index ea31a52e7..224b53b5e 100644 --- a/.github/workflows/publish-maven.yml +++ b/.github/workflows/publish-maven.yml @@ -1,4 +1,4 @@ -name: Publish Java SDK to Maven Central +name: Publish to Maven Central env: HUSKY: 0 @@ -91,6 +91,27 @@ jobs: echo "- **Release version:** $RELEASE_VERSION" >> $GITHUB_STEP_SUMMARY echo "- **Next development version:** $DEV_VERSION" >> $GITHUB_STEP_SUMMARY + - name: Update documentation with release version + id: update-docs + run: | + VERSION="${{ steps.versions.outputs.release_version }}" + + # Update version in README.md + sed -i "s|[0-9]*\.[0-9]*\.[0-9]*|${VERSION}|g" README.md + sed -i "s|copilot-sdk:[0-9]*\.[0-9]*\.[0-9]*|copilot-sdk:${VERSION}|g" README.md + + # Update version in jbang-example.java + sed -i "s|copilot-sdk:[0-9]*\.[0-9]*\.[0-9]*|copilot-sdk:${VERSION}|g" jbang-example.java + + # Commit the documentation changes before release:prepare (requires clean working directory) + git add README.md jbang-example.java + git commit -m "docs: update version references to ${VERSION}" + + # Save the commit SHA for potential rollback + echo "docs_commit_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + git push origin main + - name: Prepare Release run: | mvn -B release:prepare \ @@ -114,25 +135,15 @@ jobs: MAVEN_PASSWORD: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} - - name: Update documentation with the released version + - name: Rollback documentation commit on failure + if: failure() && steps.update-docs.outputs.docs_commit_sha != '' run: | - VERSION="${{ steps.versions.outputs.release_version }}" - - # Update version in README.md - sed -i "s|[0-9]*\.[0-9]*\.[0-9]*|${VERSION}|g" README.md - sed -i "s|copilot-sdk:[0-9]*\.[0-9]*\.[0-9]*|copilot-sdk:${VERSION}|g" README.md - - # Update version in jbang-example.java - sed -i "s|copilot-sdk:[0-9]*\.[0-9]*\.[0-9]*|copilot-sdk:${VERSION}|g" jbang-example.java - - # Stage the documentation changes (will be committed by release:prepare) - git add README.md jbang-example.java - - # Commit - git commit -m "Update documentation for version ${VERSION}" - - # Push + echo "Release failed, rolling back documentation commit..." + git revert --no-edit ${{ steps.update-docs.outputs.docs_commit_sha }} git push origin main + + # Also run Maven release:rollback to clean up any partial release state + mvn -B release:rollback || true github-release: name: Create GitHub Release @@ -149,38 +160,25 @@ jobs: GROUP_ID="io.github.copilot-community-sdk" ARTIFACT_ID="copilot-sdk" - RELEASE_NOTES=$(cat < - ${GROUP_ID} - ${ARTIFACT_ID} - ${VERSION} - - \`\`\` - - ## Gradle (Kotlin DSL) - \`\`\`kotlin - implementation("${GROUP_ID}:${ARTIFACT_ID}:${VERSION}") - \`\`\` - - ## Gradle (Groovy DSL) - \`\`\`groovy - implementation '${GROUP_ID}:${ARTIFACT_ID}:${VERSION}' - \`\`\` - - EOF - ) - - gh release create "v${VERSION}" \ - ${{ inputs.prerelease == true && '--prerelease' || '' }} \ - --title "Copilot Java SDK ${VERSION}" \ - --notes "${RELEASE_NOTES}" \ - --generate-notes \ - --target "v${VERSION}" + # Generate release notes from template + export VERSION GROUP_ID ARTIFACT_ID + RELEASE_NOTES=$(envsubst < .github/workflows/notes.template) + + # Get the previous tag for generating notes + PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "") + + # Build the gh release command + GH_ARGS=("v${VERSION}") + GH_ARGS+=("--title" "Copilot Java SDK ${VERSION}") + GH_ARGS+=("--notes" "${RELEASE_NOTES}") + GH_ARGS+=("--generate-notes") + + if [ -n "$PREV_TAG" ]; then + GH_ARGS+=("--notes-start-tag" "$PREV_TAG") + fi + + ${{ inputs.prerelease == true && 'GH_ARGS+=("--prerelease")' || '' }} + + gh release create "${GH_ARGS[@]}" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/site.yml b/.github/workflows/site.yml deleted file mode 100644 index d5827da69..000000000 --- a/.github/workflows/site.yml +++ /dev/null @@ -1,60 +0,0 @@ -# Simple workflow for deploying static content to GitHub Pages -name: Deploy Maven Site to Pages - -on: - # Runs on pushes targeting the default branch - push: - branches: ["main"] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: read - pages: write - id-token: write - -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. -concurrency: - group: "pages" - cancel-in-progress: false - -jobs: - # Build Maven Site - build: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Set up JDK 17 - uses: actions/setup-java@v5 - with: - java-version: '17' - distribution: 'temurin' - - - name: Build Maven Site with Javadoc - run: ./mvnw clean site -DskipTests -Dcheckstyle.skip=true - - - name: Setup Pages - uses: actions/configure-pages@v5 - - - name: Upload artifact - uses: actions/upload-pages-artifact@v4 - with: - # Upload the generated site - path: 'target/site' - - # Deploy to GitHub Pages - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: build - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 diff --git a/.lastmerge b/.lastmerge new file mode 100644 index 000000000..0dd87b55d --- /dev/null +++ b/.lastmerge @@ -0,0 +1 @@ +19a1d09619e695bdec81d331ec5fb160b585ecbb \ No newline at end of file diff --git a/README.md b/README.md index e72f146e9..f3494793f 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ [![Java 17+](https://img.shields.io/badge/Java-17%2B-blue)](https://openjdk.org/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -Java SDK for programmatic control of GitHub Copilot CLI. +> ⚠️ **Disclaimer:** This is an **unofficial, community-driven, and agentic-developed SDK** and is **not supported or endorsed by GitHub**. This SDK may change in breaking ways. This SDK may change in breaking ways. Use at your own risk. -> **Note:** This SDK may change in breaking ways. +Java SDK for programmatic control of GitHub Copilot CLI.

    Copilot SDK for Java @@ -15,15 +15,15 @@ Java SDK for programmatic control of GitHub Copilot CLI. ## Table of Contents -- [Requirements](#requirements) -- [Installation](#installation) -- [Quick Start](#quick-start) -- [Try it with JBang](#try-it-with-jbang) -- [Documentation](#documentation) -- [Building and Testing](#building-and-testing) -- [Projects Using This SDK](#projects-using-this-sdk) -- [Contributing](#contributing) -- [License](#license) +- [Requirements](#Requirements) +- [Installation](#Installation) +- [Quick Start](#Quick_Start) +- [Try it with JBang](#Try_it_with_JBang) +- [Documentation](#Documentation) +- [Building and Testing](#Building_and_Testing) +- [Projects Using This SDK](#Projects_Using_This_SDK) +- [Contributing](#Contributing) +- [License](#License) ## Requirements @@ -41,7 +41,7 @@ Run `mvn install` locally, then configure the dependency in your project. io.github.copilot-community-sdk copilot-sdk - 1.0.1 + 1.0.3 ``` @@ -50,12 +50,12 @@ Run `mvn install` locally, then configure the dependency in your project. Groovy: ```groovy -implementation 'io.github.copilot-community-sdk:copilot-sdk:1.0.1' +implementation 'io.github.copilot-community-sdk:copilot-sdk:1.0.3' ``` Kotlin ```kotlin -implementation("io.github.copilot-community-sdk:copilot-sdk:1.0.1") +implementation("io.github.copilot-community-sdk:copilot-sdk:1.0.3") ``` ## Quick Start @@ -117,7 +117,7 @@ jbang jbang-example.java The `jbang-example.java` file includes the dependency declaration and can be run directly: ```java -//DEPS io.github.copilot-community-sdk:copilot-sdk:1.0.1 +//DEPS io.github.copilot-community-sdk:copilot-sdk:1.0.3 ``` ## Documentation diff --git a/jbang-example.java b/jbang-example.java index e58f21b6c..5992f8be6 100644 --- a/jbang-example.java +++ b/jbang-example.java @@ -1,5 +1,5 @@ -//DEPS io.github.copilot-community-sdk:copilot-sdk:1.0.1 +//DEPS io.github.copilot-community-sdk:copilot-sdk:1.0.3 import com.github.copilot.sdk.*; import com.github.copilot.sdk.events.*; import com.github.copilot.sdk.json.*; diff --git a/pom.xml b/pom.xml index b2b8c61ff..d23c26255 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ io.github.copilot-community-sdk copilot-sdk - 1.0.2 + 1.0.3 jar GitHub Copilot Community SDK :: Java @@ -33,8 +33,8 @@ scm:git:https://github.com/copilot-community-sdk/copilot-sdk-java.git scm:git:https://github.com/copilot-community-sdk/copilot-sdk-java.git https://github.com/copilot-community-sdk/copilot-sdk-java - v1.0.2 - + v1.0.3 + 17 diff --git a/src/site/markdown/documentation.md b/src/site/markdown/documentation.md index 9c8cb119d..17f4e9684 100644 --- a/src/site/markdown/documentation.md +++ b/src/site/markdown/documentation.md @@ -1,5 +1,7 @@ # Copilot SDK for Java - Documentation +> ⚠️ **Disclaimer:** This is an **unofficial, community-driven SDK** and is **not supported or endorsed by GitHub**. Use at your own risk. + This document provides detailed API reference and usage examples for the Copilot SDK for Java. ## Table of Contents @@ -19,6 +21,7 @@ This document provides detailed API reference and usage examples for the Copilot - [Bring Your Own Key (BYOK)](#Bring_Your_Own_Key_.28BYOK.29) - [Permission Handling](#Permission_Handling) - [Infinite Sessions](#Infinite_Sessions) + - [MCP Servers](#MCP_Servers) - [Error Handling](#Error_Handling) ## API Reference @@ -486,6 +489,27 @@ var session = client.createSession( // session.getWorkspacePath() will return null ``` +### MCP Servers + +The Copilot SDK can integrate with MCP servers (Model Context Protocol) to extend the assistant's capabilities with external tools. MCP servers run as separate processes and expose tools that Copilot can invoke during conversations. + +📖 **[Full MCP documentation →](mcp.md)** - Learn about local vs remote servers, all configuration options, and troubleshooting. + +Quick example: + +```java +Map filesystemServer = new HashMap<>(); +filesystemServer.put("type", "local"); +filesystemServer.put("command", "npx"); +filesystemServer.put("args", List.of("-y", "@modelcontextprotocol/server-filesystem", "/tmp")); +filesystemServer.put("tools", List.of("*")); + +var session = client.createSession( + new SessionConfig() + .setMcpServers(Map.of("filesystem", filesystemServer)) +).get(); +``` + ## Error Handling ```java diff --git a/src/site/markdown/mcp.md b/src/site/markdown/mcp.md new file mode 100644 index 000000000..eaeb30b51 --- /dev/null +++ b/src/site/markdown/mcp.md @@ -0,0 +1,188 @@ +# Using MCP Servers with the Copilot SDK for Java + +The Copilot SDK can integrate with **MCP servers** (Model Context Protocol) to extend the assistant's capabilities with external tools. MCP servers run as separate processes and expose tools (functions) that Copilot can invoke during conversations. + +## What is MCP? + +[Model Context Protocol (MCP)](https://modelcontextprotocol.io/) is an open standard for connecting AI assistants to external tools and data sources. MCP servers can: + +- Execute code or scripts +- Query databases +- Access file systems +- Call external APIs +- And much more + +## Server Types + +The SDK supports two types of MCP servers: + +| Type | Description | Use Case | +|------|-------------|----------| +| **Local/Stdio** | Runs as a subprocess, communicates via stdin/stdout | Local tools, file access, custom scripts | +| **HTTP/SSE** | Remote server accessed via HTTP | Shared services, cloud-hosted tools | + +## Configuration + +MCP servers are configured using `Map` where keys are server names and values are configuration maps. + +### Java + +```java +import com.github.copilot.sdk.*; +import com.github.copilot.sdk.json.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +try (var client = new CopilotClient()) { + client.start().get(); + + // Create MCP server configurations + Map mcpServers = new HashMap<>(); + + // Local MCP server (stdio) + Map localServer = new HashMap<>(); + localServer.put("type", "local"); + localServer.put("command", "node"); + localServer.put("args", List.of("./mcp-server.js")); + localServer.put("env", Map.of("DEBUG", "true")); + localServer.put("cwd", "./servers"); + localServer.put("tools", List.of("*")); // "*" = all tools, empty = none + mcpServers.put("my-local-server", localServer); + + // Remote MCP server (HTTP) + Map remoteServer = new HashMap<>(); + remoteServer.put("type", "http"); + remoteServer.put("url", "https://api.githubcopilot.com/mcp/"); + remoteServer.put("headers", Map.of("Authorization", "Bearer ${TOKEN}")); + remoteServer.put("tools", List.of("*")); + mcpServers.put("github", remoteServer); + + var session = client.createSession( + new SessionConfig() + .setModel("gpt-5") + .setMcpServers(mcpServers) + ).get(); + + // Use the session with MCP tools available + var response = session.sendAndWait("List my recent GitHub notifications").get(); + System.out.println(response.getData().getContent()); +} +``` + +## Quick Start: Filesystem MCP Server + +Here's a complete working example using the official [`@modelcontextprotocol/server-filesystem`](https://www.npmjs.com/package/@modelcontextprotocol/server-filesystem) MCP server: + +```java +import com.github.copilot.sdk.*; +import com.github.copilot.sdk.json.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class McpExample { + public static void main(String[] args) throws Exception { + try (var client = new CopilotClient()) { + client.start().get(); + + // Create filesystem MCP server configuration + Map filesystemServer = new HashMap<>(); + filesystemServer.put("type", "local"); + filesystemServer.put("command", "npx"); + filesystemServer.put("args", List.of("-y", "@modelcontextprotocol/server-filesystem", "/tmp")); + filesystemServer.put("tools", List.of("*")); + + Map mcpServers = new HashMap<>(); + mcpServers.put("filesystem", filesystemServer); + + // Create session with filesystem MCP server + var session = client.createSession( + new SessionConfig() + .setMcpServers(mcpServers) + ).get(); + + System.out.println("Session created: " + session.getSessionId()); + + // The model can now use filesystem tools + var result = session.sendAndWait("List the files in the allowed directory").get(); + System.out.println("Response: " + result.getData().getContent()); + + session.close(); + } + } +} +``` + +**Output:** +``` +Session created: 18b3482b-bcba-40ba-9f02-ad2ac949a59a +Response: The allowed directory is `/tmp`, which contains various files +and subdirectories including temporary system files, log files, and +directories for different applications. +``` + +> **Tip:** You can use any MCP server from the [MCP Servers Directory](https://github.com/modelcontextprotocol/servers). Popular options include `@modelcontextprotocol/server-github`, `@modelcontextprotocol/server-sqlite`, and `@modelcontextprotocol/server-puppeteer`. + +## Configuration Options + +### Local/Stdio Server + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| `type` | `"local"` or `"stdio"` | No | Server type (defaults to local) | +| `command` | `String` | Yes | Command to execute | +| `args` | `List` | Yes | Command arguments | +| `env` | `Map` | No | Environment variables | +| `cwd` | `String` | No | Working directory | +| `tools` | `List` | No | Tools to enable (`["*"]` for all, `[]` for none) | +| `timeout` | `Integer` | No | Timeout in milliseconds | + +### Remote Server (HTTP/SSE) + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| `type` | `"http"` or `"sse"` | Yes | Server type | +| `url` | `String` | Yes | Server URL | +| `headers` | `Map` | No | HTTP headers (e.g., for auth) | +| `tools` | `List` | No | Tools to enable | +| `timeout` | `Integer` | No | Timeout in milliseconds | + +## Troubleshooting + +### Tools not showing up or not being invoked + +1. **Verify the MCP server starts correctly** + - Check that the command and args are correct + - Ensure the server process doesn't crash on startup + - Look for error output in stderr + +2. **Check tool configuration** + - Make sure `tools` is set to `["*"]` or lists the specific tools you need + - An empty list `[]` means no tools are enabled + +3. **Verify connectivity for remote servers** + - Ensure the URL is accessible + - Check that authentication headers are correct + +### Common issues + +| Issue | Solution | +|-------|----------| +| "MCP server not found" | Verify the command path is correct and executable | +| "Connection refused" (HTTP) | Check the URL and ensure the server is running | +| "Timeout" errors | Increase the `timeout` value or check server performance | +| Tools work but aren't called | Ensure your prompt clearly requires the tool's functionality | + +### Debugging tips + +1. **Enable verbose logging** in your MCP server to see incoming requests +2. **Test your MCP server independently** before integrating with the SDK +3. **Start with a simple tool** to verify the integration works + +## Related Resources + +- [Model Context Protocol Specification](https://modelcontextprotocol.io/) +- [MCP Servers Directory](https://github.com/modelcontextprotocol/servers) - Community MCP servers +- [GitHub MCP Server](https://github.com/github/github-mcp-server) - Official GitHub MCP server +- [Copilot SDK for Java Documentation](documentation.md) - SDK basics and custom tools diff --git a/src/site/site.xml b/src/site/site.xml index 3fcc921fd..0825284ee 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -15,14 +15,118 @@ + true + + GitHub Copilot SDK for Java + index.html + + + copilot-community-sdk/copilot-sdk-java + right + gray + + ]]> +

    + + (function() { + // Detect current version from URL path + var path = window.location.pathname; + var currentVersion = 'latest'; + var isSnapshot = false; + + // Check if we're in a versioned path + var versionMatch = path.match(/\/(\d+\.\d+\.\d+)\//); + var snapshotMatch = path.match(/\/snapshot\//); + + if (versionMatch) { + currentVersion = versionMatch[1]; + } else if (snapshotMatch) { + currentVersion = 'snapshot'; + isSnapshot = true; + } + + // Create disclaimer banner + var disclaimerBanner = document.createElement('div'); + disclaimerBanner.className = 'disclaimer-banner'; + disclaimerBanner.innerHTML = '⚠️ Disclaimer: This is an unofficial, community-driven SDK and is not supported or endorsed by GitHub. Use at your own risk.'; + + // Create version banner + var versionBanner = document.createElement('div'); + versionBanner.className = 'version-banner'; + versionBanner.innerHTML = '📚 Documentation version: ' + currentVersion + ' | ' + + 'View all versions | ' + + 'Latest release' + + (currentVersion !== 'snapshot' ? ' | Development (snapshot)' : ''); + + // Create snapshot warning + var snapshotWarning = document.createElement('div'); + snapshotWarning.className = 'snapshot-warning'; + snapshotWarning.innerHTML = '🚧 Development Documentation: You are viewing documentation for unreleased features. Some APIs may change before the next release.'; + if (isSnapshot) { + snapshotWarning.style.display = 'block'; + } + + // Insert banners at the top of the body + var body = document.body; + body.insertBefore(snapshotWarning, body.firstChild); + body.insertBefore(versionBanner, body.firstChild); + body.insertBefore(disclaimerBanner, body.firstChild); + })(); + + ]]> +
    @@ -34,6 +138,7 @@ + @@ -52,6 +157,8 @@ + +