Skip to content

feat(apps): connector-github, GitHub's remote MCP server as an open-edition connector app (E4 batch 1, GitHub half) - #28306

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-28276-connector-apps-batch1
Oct 31, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-28276-connector-apps-batch1

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #28276
Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:新增 npm 私有包 packages/apps/connector-github,一个开源版连接器应用:把 GitHub 的远程 MCP 服务器声明为 mcp 连接器(只读请求头,白名单只放 search_issues、issue_read 两个读工具),令牌经 credentialRef 由管理员提供、不进元数据;附两个用它的流程、上架文件和写给管理员的 README。
  • 为什么改:这是卡上 E4 第一批的 GitHub 一半。Stripe 一半没有放进来:规范化脚本的 --include-pattern 只算出白名单、不裁剪文档,实测输出 63,317,655 字节、612 个操作(用到的只有 6 个),卡上要求的「脚本打印的 sha256」和「保持小」做不到同时成立,走法等卡上裁决,所以本 PR 用 Part of 不关卡。
  • 风险与回滚:只新增一个私有包和锁文件里它的条目,不动任何已发布包、门禁或文档;回滚就是删掉这个目录再 pnpm install。
  • 先看:① packages/apps/connector-github/src/connectors/github.connector.ts(URL、请求头、白名单、凭据引用);② packages/apps/connector-github/test/boot.test.ts(假 MCP 客户端上的整栈启动,走一遍管理员设凭据的路径);③ packages/apps/connector-github/README.md 的 Credentials 一节。
  • 两分钟验证:pnpm --filter @objectstack/connector-app-github test,应看到 Validation passed 和 Tests 10 passed (10)。
  • 需要你做的:无,席位落地。

What changes

One new npm-private workspace package under the existing packages/apps/* glob, @objectstack/connector-app-github (packages/apps/connector-github/), and its entry in pnpm-lock.yaml. No other file changes.

  • The connector (src/connectors/github.connector.ts), written as recipe 2 of skills/objectstack-automation/references/integration-recipes.md teaches: provider: 'mcp', http transport to https://api.githubcopilot.com/mcp/ with the X-MCP-Readonly: true header, providerConfig.include: ['search_issues', 'issue_read'], and auth: { type: 'bearer', credentialRef: 'GITHUB_MCP_TOKEN' }. The URL, the read-only header and the Authorization: Bearer TOKEN form come from GitHub's own documentation (github/github-mcp-server at f2a07cd1, docs/remote-server.md and the README's remote configuration), and the repo scope both tools need comes from the same README's per-tool scope lines.
  • Two flows that use it (src/flows/github.flows.ts): github_search_issues (input query, output search.content) and github_get_issue (inputs owner, repo, issue_number, output issue.content).
  • The stack (objectstack.config.ts): type: 'app', namespace github, requires: ['automation'], and the zero-argument ConnectorMcpPlugin in plugins:, the way the scaffolded template lists its executors.
  • The listing file (objectstack.manifest.json) and the README the listing points at. Its credentials section names GITHUB_MCP_TOKEN, where to get it and the permissions it needs, as the marketplace handbook asks.
  • Validated by this repository's CI at HEAD. The package's test script is objectstack validate && vitest run, so the run CI makes of test runs os validate first. No CI step runs a package's validate script on its own.

Tests

On head cf30e616: pnpm --filter @objectstack/connector-app-github test gives Validation passed, then Test Files 2 passed (2) and Tests 10 passed (10). pnpm --filter @objectstack/connector-app-github typecheck exits 0.

  • test/boot.test.ts boots the app with bootStack (@objectstack/verify), which is how objectstack serve composes it. The only substitution is the MCP client, swapped in by plugin name through extraPlugins, so no call reaches GitHub. It checks the administrator's path in order:
    1. With no credential set, github_mcp is degraded, the reason names GITHUB_MCP_TOKEN, and nothing is dialled.
    2. A flow run in that state answers 400 FLOW_FAILED, the message names the reference, and nothing is dialled.
    3. PUT /api/v1/automation/connectors/credential-refs/GITHUB_MCP_TOKEN brings the instance to ready. Its actions are exactly issue_read and search_issues, although the fake server also lists issue_write. The transport dialled is GitHub's URL with X-MCP-Readonly: true and Authorization: Bearer plus the fixture token.
    4. Both flows dispatch their tool with the inputs they were given, through POST /api/v1/automation/NAME/trigger.
  • test/listing.test.ts checks the listing file. It parses against TemplateManifestSchema, its manifestId equals the stack's manifest.id, and it declares no key that publish ignores. The README's ## Credentials section names every credentialRef the stack declares, and no connector carries a secret.
  • The vitest config aliases every workspace import to source and declares OS_REGISTRY_LOG: 'warn'. The suite's [Registry] lines went from 247 to 0.

Acceptance notes

  • The Stripe half is not in this PR. It needs a decision, which the report on connector apps, E4 batch 1 (static key): GitHub over its remote MCP server and Stripe read over its OpenAPI document, as open-edition metadata packages validated by this repository's CI #28276 lays out. The measurement: normalize-openapi --in Stripe spec3.json at 51610f5f, with an include pattern selecting 6 GET operations, writes 63,317,655 bytes holding all 612 operations, sha256 0ea3c37a…. --include-pattern computes the include list and trims nothing, and --max-depth 0 still writes 6,070,551 bytes with all 612 operations. So "the packaged spec is the script's output, its sha256 in the README" and recipe 1's "trim the vendor's document to the operations the app calls" cannot both hold with the script as it is.
  • A classic token that lacks the repo scope. GitHub's server hides every tool the token's scopes do not cover (docs/scope-filtering.md), and an include entry the server does not list is a configuration fault. I probed this once, through bootStack against a fake server listing only get_me; the probe is not committed. With the value in the environment variable, the whole boot is refused. With the value in the credential store, the instance stays degraded and is told to "fix its declaration". A second PUT with a well-scoped value recovers it with no restart. The README states both outcomes as measured.
  • No live call. I hold no GitHub token for this, so the live call against GitHub's server is NOT MEASURED.
  • The listing file is not covered by the repository gate. check:template-manifests walks only packages/create-objectstack/src/templates, so this file is parsed by the package's own listing.test.ts instead.
  • The listing has no icon. The handbook makes an icon a requirement only for a marketplace submission, and listing generation is cloud-repository work.

Verification

Gate families that read this diff (60 of 60, all exit 0, head cf30e61)

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 60 commands (48 by path, 6 by change kind, 8 whole-tree). Every one ran with its exit code captured before any pipe. --ran reconciliation: 60 derived, 60 run, 0 NOT-MEASURED, 0 UNRUN, a derived zero.

On the first run (head fbb7b6a2), two gates were red on this package's test setup: check:test-source-alias (dist-resolved test imports) and check:registry-log-declared (a bootStack suite with no OS_REGISTRY_LOG). Both are fixed in cf30e616 and green. None of the 60 assumes a published platform app: check:published-files and check:changeset-fixed both pass with the package private.

Not run locally

test:repo and the repository-wide scans (pnpm lint first among them) belong to CI. The dogfood battery does not apply: the title has no ! and there is no narrowing arm.


Generated by Claude Code

…dition connector app

A declarative mcp instance (http transport, X-MCP-Readonly, include
allowlist of search_issues and issue_read), a bearer credentialRef, two
flows using it, the listing file and an administrator README. Tested by a
boot through bootStack against a fake MCP client.

Claude-Session: https://claude.ai/code/session_017xRBcVBKAFk1ntXSjvPt7U
Co-authored-by: Claude <noreply@anthropic.com>
…pins the unset-credential run

ESM package (import.meta under NodeNext); the README states what an
under-scoped classic token does, as measured on both credential paths.
The lockfile drops the Stripe importer: that app waits on a decision.

Claude-Session: https://claude.ai/code/session_017xRBcVBKAFk1ntXSjvPt7U
Co-authored-by: Claude <noreply@anthropic.com>
…ce and quiet the registry log

check:test-source-alias and check:registry-log-declared, both red on the
first gate run, both green after.

Claude-Session: https://claude.ai/code/session_017xRBcVBKAFk1ntXSjvPt7U
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 31, 2026
@github-actions github-actions Bot added size/l dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation tests tooling labels Oct 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/connector-app-github, touching 18 documentable anchor(s). ⚠️ 5 changed file(s) yielded no anchor (packages/apps/connector-github/README.md, packages/apps/connector-github/objectstack.config.ts, packages/apps/connector-github/objectstack.manifest.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

5 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/automation/connectors.mdx (via providerConfig (symbol, a field of const object GitHubMcpConnector), connector_action (literal, a string literal in nodes))
  • content/docs/automation/flows.mdx (via connector_action (literal, a string literal in nodes))
  • content/docs/data-modeling/external-datasources.mdx (via providerConfig (symbol, a field of const object GitHubMcpConnector))
  • content/docs/data-modeling/odata-datasources.mdx (via providerConfig (symbol, a field of const object GitHubMcpConnector))
  • content/docs/permissions/tenant-audit-census.mdx (via HERE (symbol, a top-level const object))

⛔ 5 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v15.mdx (via providerConfig (symbol, a field of const object GitHubMcpConnector), connector_action (literal, a string literal in nodes))
  • content/docs/releases/v17/17-0.mdx (via connector_action (literal, a string literal in nodes))
  • content/docs/releases/v17/17-5.mdx (via connector_action (literal, a string literal in nodes))
  • content/docs/releases/v17/17-6.mdx (via connector_action (literal, a string literal in nodes))
  • content/docs/releases/v18/18-0.mdx (via connector_action (literal, a string literal in nodes))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 5 changed file(s) yielded no anchor (packages/apps/connector-github/README.md, packages/apps/connector-github/objectstack.config.ts, packages/apps/connector-github/objectstack.manifest.json, …) — pages documenting those are invisible to this run
  • 7 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 59 of 211 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 53749673ba7a0005d09e8a79544da10d5faa10bc → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 4ebf1b9a79269f78b5859e68b4c038cae2b3ed97 — the merge of head cf30e616111bfe1b44a6a1a5bf203319ba8f33cf into base 53749673ba7a0005d09e8a79544da10d5faa10bc, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 4ebf1b9a79269f78b5859e68b4c038cae2b3ed97 && git checkout 4ebf1b9a79269f78b5859e68b4c038cae2b3ed97
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 53749673ba7a0005d09e8a79544da10d5faa10bc cf30e616111bfe1b44a6a1a5bf203319ba8f33cf && git checkout -B drift-repro 53749673ba7a0005d09e8a79544da10d5faa10bc && git merge --no-ff cf30e616111bfe1b44a6a1a5bf203319ba8f33cf

node scripts/docs-audit/affected-docs.mjs --json 53749673ba7a0005d09e8a79544da10d5faa10bc

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 53749673ba7a0005d09e8a79544da10d5faa10bc → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

epic:#27293#1 · sunhaolin · session_017xRBcVBKAFk1ntXSjvPt7U

席位核对

ACCEPT at head cf30e616 · Implemented-by: claude/issue-28276-connector-apps-batch1 · Reviewed-by: this seat. No contract-review record is owed: Clause-②: no, no packages/spec/src/** path, no governed path (check-governed-merges --pr 28306: NOT governed).

Check Result
Shape draft, base main, first line Part of #28276 (the Stripe half waits on #28309); no closing keyword
Scope 11 files, +568/−0, all inside the claim's surface: packages/apps/connector-github/** (npm-private) and pnpm-lock.yaml; skip-changeset right (nothing in any published files[] moves)
Gates 69 check-runs on the head: 57 success, 12 skipped (all on main's roster, check-expected-skips OK); mergeable clean
Evidence objectstack validate + 10 tests: a real bootStack boot with a fake MCP client (degraded without the credential, naming GITHUB_MCP_TOKEN; ready with exactly [issue_read, search_issues] after PUT credential-refs/… although the fake also lists issue_write), and a listing test (manifest parses, manifestId = stack id, every credentialRef named in the README). Live GitHub call: NOT MEASURED (no credential held)
Metadata provider: 'mcp', http transport to GitHub's documented endpoint, X-MCP-Readonly: true, include: ['search_issues', 'issue_read'], bearer with credentialRef — no secret in metadata; every key delivered at base
README, read line by line the credentials page and install-dialog step (#27238, merged), "an environment variable … takes precedence over a stored value" (the env → store → vault chain of #27237), and the under-scoped classic-token paragraph, which states today's behaviour exactly — #28311 changes that behaviour and owes this paragraph

Open items, not this PR: listing identity app.objectstack.connector-github → #28313 (needs-user-decision, blocks the first publish, not this merge) · the Stripe app → after #28309.

Landing: not governed: the seat lands it now (pr_ready + automerge_enable).

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 31, 2026 04:27
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 31, 2026 04:27
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 31, 2026
Merged via the queue into main with commit 7a0481a Oct 31, 2026
70 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-28276-connector-apps-batch1 branch October 31, 2026 05:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/l skip-changeset PR has no user-facing published change; bypasses the changeset gate tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant