Skip to content

chore(sample-code): replace deprecated GitHub MCP server with native-fetch tools - #2153

Open
InjunPark-sap wants to merge 17 commits into
mainfrom
chore/support-bot-github-tools
Open

chore(sample-code): replace deprecated GitHub MCP server with native-fetch tools#2153
InjunPark-sap wants to merge 17 commits into
mainfrom
chore/support-bot-github-tools

Conversation

@InjunPark-sap

@InjunPark-sap InjunPark-sap commented Aug 12, 2026

Copy link
Copy Markdown
Member

Context

Closes SAP/ai-sdk-js-backlog#641.

What this PR does and why it is needed

Replaces the deprecated @modelcontextprotocol/server-github MCP with four native-fetch tools (github-tools.ts), reusing the same tool names so the agent loop is untouched.
Repo scope is enforced in the URL — the model only passes a term / issue number / path, never owner, repo, or a raw query, so scope-widening tricks can't work. No new dependency.

Also folds in the post-merge follow-ups: throwOnLoadError: false + context7 libraryId allowlist (agent.ts), head+tail code-block truncation (reply.ts), and knowledge entries for prompt-registry / document-grounding / llm-batch (knowledge.ts).

No Changeset (private package), no REUSE header (wildcard), no public API change.

@InjunPark-sap

Copy link
Copy Markdown
Member Author

Deprecated @modelcontextprotocol/server-github MCP → four native-fetch tools (github-tools.ts), same tool names so the agent loop is untouched. context7 MCP retained.

Guardrail: repo scope enforced at the API call — repo hardcoded in every URL, scopedQuery() strips injected repo:/org:/user: + booleans. Model passes only a term / issue number / path, so scope-widening is impossible.

@InjunPark-sap InjunPark-sap changed the title chore(sample-code): replace deprecated GitHub MCP server with native-… chore(sample-code): replace deprecated GitHub MCP server with native-fetch tools Aug 13, 2026
function scopedQuery(term: string): string {
const stripped = (term ?? '')
.replace(/(?:^|\s)(?:repo|org|user):[^\s]*/gi, '')
.replace(/\b(?:OR|AND|NOT)\b/g, '')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verify this, but it might also be possible to add 5 AND/NOT/OR instead, any more and searches are rejected. (e.g. AND repo:sap/ai-sdk-js).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified. The 5-operator cap is real (422 on the 6th). But the only actual scope-bypass is an injected repo:/org:/user: qualifierrepo:SAP/ai-sdk-js foo OR repo:golang/go bar leaks the other repo (9754 hits in my test).

Bare OR/AND/NOT never widen scope; the leading repo: binds the whole query. So I dropped the OR/AND/NOT strip and kept only the qualifier strip — boolean search works now, GitHub's 5-op limit handles the rest.

Comment thread sample-code/src/support-bot/github-tools.ts Outdated
@InjunPark-sap

Copy link
Copy Markdown
Member Author

context7 → hosted HTTP transport

Per David's review: switched context7 from local stdio spawn to the hosted HTTP endpoint (https://mcp.context7.com/mcp, the variant listed in mcp-registry).

  • Dropped @upstash/context7-mcp devDep — no local binary to spawn.
  • API key optional (free tier works); CONTEXT7_API_KEY header wired for higher rate limits when needed.
  • Side effect: removes the stdio ENOENT failure mode — a broken/missing binary can no longer kill the run.
  • Also resolved & allowlisted the SAP AI SDK llms.txt libraryId (/llmstxt/sap_github_io_ai-sdk_llms_txt, 1305 snippets).

Verified: tsc + lint pass; --frozen-lockfile clean; local reply.ts run loads resolve-library-id, query-docs from the hosted endpoint and cites real issues. Lockfile diff is context7-removal only (canary snapshot churn excluded).

@InjunPark-sap

Copy link
Copy Markdown
Member Author

Widened the issue-reply trigger to all opened issues (was MEMBER/OWNER/COLLABORATOR only).

It's an open-source repo — most support questions are from external users, exactly who the old filter skipped. Abuse surface stays bounded: Slack-only, no auto issue reply, so a spam filing costs one model call + one Slack line. Per-issue concurrency guard unchanged.

@InjunPark-sap
InjunPark-sap marked this pull request as ready for review August 14, 2026 14:02
@hyperspace-pr-bot

hyperspace-pr-bot Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Summary

The following content is AI-generated and provides a summary of the pull request:


Replace Deprecated GitHub MCP Server with Native-Fetch Tools

Context

Closes SAP/ai-sdk-js-backlog#641.

What this PR does and why it is needed

Replaces the deprecated @modelcontextprotocol/server-github MCP server with four native-fetch tools implemented in github-tools.ts. The new tools preserve the same tool names (github__search_issues, github__get_issue, github__search_code, github__get_file_contents) so the agent loop remains unchanged, while removing a deprecated dependency.

Key changes

sample-code/src/support-bot/github-tools.ts (new file)

  • Implements four GitHub tools using native fetch against the GitHub REST API
  • Repo scope (SAP/ai-sdk-js) is hardcoded in every URL — the model only passes keywords, an issue number, or a file path; never owner, repo, or raw query strings
  • Defense-in-depth: scopedQuery() strips competing repo:/org:/user: qualifiers, and results are filtered server-side to enforce scope even against unforeseen query-syntax tricks

sample-code/src/support-bot/agent.ts

  • Imports and wires in githubTools from the new file; removes the old MCP GitHub server config and the ALLOWED_GITHUB_TOOLS allowlist + scopeToAllowedRepo() function (now handled inside the tools themselves)
  • Sets throwOnLoadError: false so a context7 hiccup degrades gracefully instead of killing the run
  • Adds a ALLOWED_LIBRARY_IDS allowlist for context7 libraryId (SAP AI SDK docs, llms.txt variant, LangChain docs)
  • Extracts a dispatchToolCall() helper and a runToolTurn() helper to share SEC-3 guards and the context7 libraryId check between the draft loop and the new self-verify pass
  • Adds a bounded self-verify loop (MAX_VERIFY_ITER = 3) after the draft so the model re-checks factual claims against source
  • Refines system prompt wording: tool descriptions now state repo scope is automatic; answer rules are tightened

sample-code/src/support-bot/knowledge.ts

  • Adds knowledge entries for @sap-ai-sdk/prompt-registry, @sap-ai-sdk/document-grounding, and @sap-ai-sdk/llm-batch
  • Adds a normalization note clarifying that the Orchestration service always returns a plain string for ChatDelta.content

sample-code/src/support-bot/reply.ts

  • Switches code-block truncation from head-only to head + tail, preserving diagnostic signal from error stack tails

sample-code/package.json / pnpm-lock.yaml

  • Removes @modelcontextprotocol/server-github and @upstash/context7-mcp dev dependencies (no new dependency added)
  • Cleans up supports-color peer-dependency suffixes across the lockfile

  • 🔄 Regenerate and Update Summary
  • ✏️ Insert as PR Description (deletes this comment)
  • 🗑️ Delete comment
PR Bot Information

Version: 1.29.26

Comment thread .github/workflows/issue-reply.yml Outdated
import { z } from 'zod';
import type { StructuredToolInterface } from '@langchain/core/tools';

// Native-fetch GitHub tools — replaces the deprecated @modelcontextprotocol/server-github.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[q] This seems odd to me. The deprecation note states that there seems to be a replacement: https://www.npmjs.com/package/@modelcontextprotocol/server-github can't we use that somehow? (=>https://github.com/github/github-mcp-server)

@InjunPark-sap InjunPark-sap Aug 17, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The successor is github/github-mcp-server which is a Go binary + hosted endpoint, not a Node drop-in.

The hosted /readonly URL fits our transport but has no single-repo scoping (only toolset/token), whereas this module hardcodes repo:SAP/ai-sdk-js so the model can't reach other repos.

Keeping that would need a fine-grained single-repo PAT — for a read-only Slack-only bot the 166-LOC module is the smaller bet.

@KavithaSiva KavithaSiva left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to try out alternate implementation with opencode that could simplify the agent loop and uses github mcp server instead. I will open another PR, and then we can evaluate.

"text": ("Support Issue: " + $url + "\n\n" + $answer)
}' > /tmp/slack-payload.json

- name: Notify Slack

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[q] Which slack channel does this get posted to?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#ai-sdk-js-support

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants