Skip to content

feat(sdk): provider SDKs for TypeScript, Python, and Go — all conformant#43

Merged
macanderson merged 4 commits into
mainfrom
feat/sdk-typescript
Jul 23, 2026
Merged

feat(sdk): provider SDKs for TypeScript, Python, and Go — all conformant#43
macanderson merged 4 commits into
mainfrom
feat/sdk-typescript

Conversation

@macanderson

@macanderson macanderson commented Jul 23, 2026

Copy link
Copy Markdown
Owner

What this is

Provider SDKs in TypeScript, Python, and Go (sdk/). Each is a
zero-dependency SDK for building Context Graph Protocol providers, and each one's
example provider passes the full Rust conformance suite — four independent
implementations (Rust + these three) now clear the bar, which is a
GOVERNANCE.md freeze criterion ("≥2 independent implementations pass
conformance").

$ .github/scripts/conformance-external.sh -- <each SDK's example provider>
  OK handshake / consent-scope / frame-validity / verify-honesty /
     budget-honesty / shutdown-clean / malformed-input-tolerance
All 7 checks passed — external provider is conformant.

They're judged by the same Rust suite that judges the reference provider,
driven language-neutrally by contextgraph-inspect stdio -- <program>.

SDK Path Validated
TypeScript sdk/typescript ✅ 7/7 locally + sdk (typescript) CI job
Python sdk/python ✅ 7/7 locally + sdk (python) CI job
Go sdk/go ✅ 7/7 locally + sdk (go) CI job

Each SDK gives you

  • Wire types mirrored from the JSON schema (the language-neutral truth).
  • runStdioProvider / run_stdio_provider / RunStdioProvider — the stdio
    lifecycle loop (handshake, query with correlation-id echo, verify, shutdown,
    and staying alive with a typed error on malformed input).
  • budgetTokens / budget_tokens / BudgetTokens — the B3 cost,
    ceil(utf8_len/4).
  • A runnable honest example provider that passes all seven checks.

Shared infrastructure

  • .github/scripts/conformance-external.sh — reusable, language-neutral oracle:
    run the conformance suite against any external provider command. Every SDK
    is validated through it, in CI.
  • Three new CI jobs (sdk (typescript|python|go)) build each SDK and assert its
    example provider stays green.

Scope note

conformance-red.sh proves the suite catches cheaters using the Rust fixture,
so an SDK provider only has to be honest (pass the 7 green checks) — it does
not reimplement the ~14 misbehaviour modes. That keeps each SDK small and focused.

Closes the SDK half of #17. #14 (host-side conformance) remains queued.

Add sdk/typescript: a zero-dependency TS SDK for building Context Graph Protocol
providers. Its example provider passes all 7 conformance checks under the same
Rust suite that judges the reference provider — the first independent (non-Rust)
implementation to clear that bar, which is a GOVERNANCE.md freeze criterion
("≥2 independent implementations pass conformance").

- src/types.ts — wire types mirrored from schema/contextgraph-envelope.schema.json.
- src/provider.ts — runStdioProvider(): the stdio lifecycle loop (handshake,
  query with correlation-id echo, verify, shutdown, malformed-input tolerance).
- src/budget.ts — budgetTokens() = ceil(utf8_len/4) (B3).
- examples/example-docs.ts — honest reference provider; passes green.
- .github/scripts/conformance-external.sh — run the suite against ANY external
  provider command; the reusable, language-neutral oracle for every SDK.
- CI: `sdk (typescript)` job builds the SDK and asserts conformance is green.

Part of #17 (provider SDKs). Python and Go follow, validated the same way; Go is
gated on a Go toolchain (not yet available locally) so it will not be presented
as working until it passes the same suite.
@vercel

vercel Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
context-graph-protocol Ready Ready Preview, Comment Jul 23, 2026 7:35pm

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry @macanderson, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

Add sdk/python: a zero-dependency (stdlib-only) Python SDK for building Context
Graph Protocol providers. Its example provider passes all 7 conformance checks
under the same Rust suite (Rust, TypeScript, now Python — three independent
implementations that clear the freeze bar).

- contextgraph_sdk/types.py — TypedDicts mirrored from the JSON schema.
- contextgraph_sdk/provider.py — run_stdio_provider(): the stdio lifecycle loop
  (handshake, query with correlation-id echo, verify, shutdown, malformed-input
  tolerance).
- contextgraph_sdk/budget.py — budget_tokens() = ceil(utf8_len/4) (B3).
- examples/example_docs.py — honest reference provider; passes green.
- CI: `sdk (python)` job asserts the Python provider stays conformant.
Comment thread sdk/typescript/src/provider.ts Outdated
Add sdk/go: a zero-dependency (stdlib-only) Go SDK for building Context Graph
Protocol providers. Its example provider passes all 7 conformance checks under
the same Rust suite (Rust, TypeScript, Python, now Go — four independent
implementations that clear the freeze bar). `go vet` and `go build` are clean.

- contextgraph/types.go — wire structs mirrored from the JSON schema.
- contextgraph/provider.go — RunStdioProvider(): the stdio lifecycle loop
  (handshake, query with correlation-id echo, verify, shutdown, malformed-input
  tolerance); the Verifier interface is the optional context/verify surface.
- contextgraph/budget.go — BudgetTokens() = ceil(utf8_len/4) (B3).
- examples/example-docs/main.go — honest reference provider; passes green.
- CI: `sdk (go)` job vets, builds, and asserts the Go provider stays conformant.
@macanderson macanderson changed the title feat(sdk): provider SDKs — TypeScript (conformant), Python + Go to follow feat(sdk): provider SDKs for TypeScript, Python, and Go — all conformant Jul 23, 2026
…ahead of prior async `query`/`verify` handlers' pending replies, silently dropping their `frames`/`verified` output.

This commit fixes the issue reported at sdk/typescript/src/provider.ts:111

## Bug

`runStdioProvider` dispatched each stdin line fire-and-forget:

```ts
rl.on("line", (line) => { void handleLine(provider, line); });
```

`handleLine` is `async` and `await`s `provider.query(...)` / `provider.verify(...)` **before** calling `writeEnvelope(...)`. readline emits all buffered lines synchronously within one tick, so when a host pipelines requests (handshake, query, verify, shutdown), the `query`/`verify` handlers suspend at their `await` — deferring their `writeEnvelope` reply to a later microtask — while the following `shutdown` handler runs and calls `process.exit(0)` synchronously (line 111), terminating the process before those queued replies are written.

### Impact

The `frames` and `verified` replies are **silently lost**. This affects pipelined hosts, which the SDK explicitly enables by advertising `correlation: true`, and any request handler doing real async work whose reply a shutdown can preempt.

### Reproduction (confirmed)

Built the shipped example and piped `handshake`, `query`, `shutdown` (one JSON envelope per line):

*   **Before fix:** output was only `handshake_ack` — the `frames` reply was dropped.
*   **After fix:** all replies (`handshake_ack`, `frames`, `verified`) are emitted.

The EOF path (`rl.on("close", () => process.exit(0))`) did not exhibit this because `close` fires on a later tick, letting microtasks drain — only the synchronous `shutdown` branch raced.

## Fix

Serialize line handling through a promise queue so each line's handler is chained after the previous one resolves:

```ts
let queue: Promise<void> = Promise.resolve();
rl.on("line", (line) => {
  queue = queue.then(() => handleLine(provider, line)).catch(() => {});
});
```

Now a later `shutdown` handler only runs — and calls `process.exit(0)` — after all prior handlers have resolved and their replies have been written. The `.catch(() => {})` keeps the chain alive if one handler rejects, so a single failing line never drops replies for subsequent lines (matching the prior independent-per-line robustness). EOF behavior and malformed-input tolerance are preserved.


Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: macanderson <mac@macanderson.com>
@macanderson
macanderson marked this pull request as ready for review July 23, 2026 19:34

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry @macanderson, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@macanderson
macanderson merged commit ef5fb52 into main Jul 23, 2026
14 checks passed
@macanderson
macanderson deleted the feat/sdk-typescript branch July 23, 2026 19:35
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.

1 participant