Skip to content

Commit 5b41efd

Browse files
authored
feat: harden MCP contracts and repository guardrails (#1)
* feat: harden mcp contracts and repository guardrails * fix: remove private xint-cloud dependency from required parity check
1 parent 9d856fa commit 5b41efd

25 files changed

+1501
-344
lines changed

.github/CODEOWNERS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
* @0xNyk
2+
/.github/workflows/* @0xNyk
3+
/scripts/* @0xNyk

.github/workflows/capability-contract.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ jobs:
1818
repository: 0xNyk/xint-rs
1919
path: xint-rs
2020

21+
2122
- name: Setup Bun
2223
uses: oven-sh/setup-bun@v2
2324

@@ -27,10 +28,19 @@ jobs:
2728
- name: Install xint deps
2829
run: bun install --frozen-lockfile
2930

31+
3032
- name: Generate manifests
3133
run: |
3234
bun run xint.ts capabilities --compact > /tmp/xint-ts-capabilities.json
3335
cargo run --manifest-path xint-rs/Cargo.toml -- capabilities --compact > /tmp/xint-rs-capabilities.json
3436
3537
- name: Check contract parity
3638
run: bun scripts/check-capability-contract.mjs /tmp/xint-ts-capabilities.json /tmp/xint-rs-capabilities.json
39+
40+
- name: Run MCP envelope contract tests (TypeScript)
41+
run: bun test lib/mcp-envelope-contract.test.ts
42+
43+
- name: Run MCP envelope contract tests (Rust)
44+
run: |
45+
cargo test --manifest-path xint-rs/Cargo.toml mcp::costs_tool_returns_success_payload_without_network
46+
cargo test --manifest-path xint-rs/Cargo.toml mcp::cache_clear_tool_returns_success_payload

.github/workflows/ci.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
11+
jobs:
12+
checks:
13+
runs-on: ubuntu-latest
14+
timeout-minutes: 15
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@v4
18+
19+
- name: Setup Bun
20+
uses: oven-sh/setup-bun@v2
21+
with:
22+
bun-version: "1.3.5"
23+
24+
- name: Install dependencies
25+
run: bun install --frozen-lockfile
26+
27+
- name: Typecheck
28+
run: bun run typecheck
29+
30+
- name: Test
31+
run: bun run test
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Guardrails Audit
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
workflow_dispatch:
9+
schedule:
10+
- cron: "15 6 * * *"
11+
12+
jobs:
13+
audit:
14+
runs-on: ubuntu-latest
15+
timeout-minutes: 10
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@v4
19+
20+
- name: Audit xint guardrails drift
21+
env:
22+
GH_TOKEN: ${{ github.token }}
23+
OWNER: 0xNyk
24+
REPOS: xint
25+
BRANCH: main
26+
run: ./scripts/audit-guardrails.sh
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
name: MCP Nightly Regression
2+
3+
on:
4+
schedule:
5+
- cron: "0 6 * * *"
6+
workflow_dispatch:
7+
8+
jobs:
9+
regression:
10+
runs-on: ubuntu-latest
11+
timeout-minutes: 25
12+
steps:
13+
- name: Checkout xint
14+
uses: actions/checkout@v4
15+
16+
- name: Checkout xint-rs
17+
uses: actions/checkout@v4
18+
with:
19+
repository: 0xNyk/xint-rs
20+
path: xint-rs
21+
22+
- name: Checkout xint-cloud
23+
uses: actions/checkout@v4
24+
with:
25+
repository: 0xNyk/xint-cloud
26+
path: xint-cloud
27+
28+
- name: Setup Bun
29+
uses: oven-sh/setup-bun@v2
30+
31+
- name: Setup Rust
32+
uses: dtolnay/rust-toolchain@stable
33+
34+
- name: Install xint deps
35+
run: bun install --frozen-lockfile
36+
37+
- name: Install xint-cloud deps
38+
run: bun install --frozen-lockfile
39+
working-directory: xint-cloud
40+
41+
- name: Generate manifests
42+
run: |
43+
bun run xint.ts capabilities --compact > /tmp/xint-ts-capabilities.json
44+
cargo run --manifest-path xint-rs/Cargo.toml -- capabilities --compact > /tmp/xint-rs-capabilities.json
45+
46+
- name: Check contract parity
47+
run: bun scripts/check-capability-contract.mjs /tmp/xint-ts-capabilities.json /tmp/xint-rs-capabilities.json
48+
49+
- name: Check release version parity
50+
run: >
51+
bun scripts/check-release-version-parity.mjs
52+
package.json
53+
xint-rs/Cargo.toml
54+
/tmp/xint-ts-capabilities.json
55+
/tmp/xint-rs-capabilities.json
56+
57+
- name: Run MCP envelope contract tests (TypeScript)
58+
run: bun test lib/mcp-envelope-contract.test.ts
59+
60+
- name: Run MCP envelope contract tests (Rust)
61+
run: |
62+
cargo test --manifest-path xint-rs/Cargo.toml mcp::costs_tool_returns_success_payload_without_network
63+
cargo test --manifest-path xint-rs/Cargo.toml mcp::cache_clear_tool_returns_success_payload
64+
65+
- name: Run package API smoke tests (xint-cloud create/query/publish)
66+
run: bun test src/server.test.ts --filter "package status/search/query/publish/refresh endpoints operate on persisted package state"
67+
working-directory: xint-cloud
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: Release Parity
2+
3+
on:
4+
release:
5+
types: [published]
6+
workflow_dispatch:
7+
8+
jobs:
9+
parity:
10+
runs-on: ubuntu-latest
11+
timeout-minutes: 20
12+
steps:
13+
- name: Checkout xint
14+
uses: actions/checkout@v4
15+
16+
- name: Checkout xint-rs
17+
uses: actions/checkout@v4
18+
with:
19+
repository: 0xNyk/xint-rs
20+
path: xint-rs
21+
22+
- name: Setup Bun
23+
uses: oven-sh/setup-bun@v2
24+
25+
- name: Setup Rust
26+
uses: dtolnay/rust-toolchain@stable
27+
28+
- name: Install xint deps
29+
run: bun install --frozen-lockfile
30+
31+
- name: Generate manifests
32+
run: |
33+
bun run xint.ts capabilities --compact > /tmp/xint-ts-capabilities.json
34+
cargo run --manifest-path xint-rs/Cargo.toml -- capabilities --compact > /tmp/xint-rs-capabilities.json
35+
36+
- name: Check capability contract parity
37+
run: bun scripts/check-capability-contract.mjs /tmp/xint-ts-capabilities.json /tmp/xint-rs-capabilities.json
38+
39+
- name: Check release version parity
40+
run: >
41+
bun scripts/check-release-version-parity.mjs
42+
package.json
43+
xint-rs/Cargo.toml
44+
/tmp/xint-ts-capabilities.json
45+
/tmp/xint-rs-capabilities.json

CONTRIBUTING.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,57 @@ lib/format.ts Output formatters (terminal + markdown)
3737
- Mention your Bun version (`bun --version`)
3838
- Don't include API keys or tokens in issue reports
3939

40+
## Branch Protection
41+
42+
`main` should enforce:
43+
- required status checks
44+
- at least 1 approving review
45+
- stale review dismissal on new commits
46+
- conversation resolution before merge
47+
- linear history
48+
- no force pushes or deletions
49+
50+
Automation script:
51+
52+
```bash
53+
cd xint
54+
./scripts/apply-branch-protection.sh --dry-run
55+
./scripts/apply-branch-protection.sh --apply
56+
```
57+
58+
Scope:
59+
- `0xNyk/xint`: `CI / checks`, `Capability Contract / parity`
60+
- `0xNyk/xint-rs`: `CI / checks`
61+
- `0xNyk/xint-cloud`: `ci / checks`
62+
63+
Repository rulesets (hard guardrails):
64+
65+
```bash
66+
cd xint
67+
./scripts/apply-repo-rulesets.sh --dry-run
68+
./scripts/apply-repo-rulesets.sh --apply
69+
```
70+
71+
Ruleset name:
72+
- `Main Branch Guardrails`
73+
74+
Code owners:
75+
- `.github/CODEOWNERS` defines required owners for all paths.
76+
- Branch protection and rulesets enforce code-owner reviews.
77+
78+
Guardrail drift audit:
79+
80+
```bash
81+
cd xint
82+
./scripts/audit-guardrails.sh
83+
```
84+
85+
Optional strict merge-queue assertion (if your GitHub tier exposes merge-queue rulesets):
86+
87+
```bash
88+
REQUIRE_MERGE_QUEUE=true ./scripts/audit-guardrails.sh
89+
```
90+
4091
## License
4192

4293
By contributing, you agree that your contributions will be licensed under the MIT License.

lib/action_result.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { ActionResultType } from "./actions";
2+
3+
export type ActionExecutionKind = Extract<ActionResultType, "success" | "error" | "info">;
4+
5+
export type ActionExecutionResult<T = unknown> = {
6+
type: ActionExecutionKind;
7+
message: string;
8+
data?: T;
9+
fallbackUsed: boolean;
10+
};
11+
12+
export function actionSuccess<T>(message: string, data?: T, fallbackUsed = false): ActionExecutionResult<T> {
13+
return { type: "success", message, data, fallbackUsed };
14+
}
15+
16+
export function actionInfo<T>(message: string, data?: T, fallbackUsed = false): ActionExecutionResult<T> {
17+
return { type: "info", message, data, fallbackUsed };
18+
}
19+
20+
export function actionError(message: string): ActionExecutionResult<never> {
21+
return { type: "error", message, fallbackUsed: false };
22+
}

lib/actions.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { describe, expect, test } from "bun:test";
2+
import {
3+
INTERACTIVE_ACTIONS,
4+
normalizeInteractiveChoice,
5+
scoreInteractiveAction,
6+
} from "./actions";
7+
8+
describe("interactive action registry", () => {
9+
test("normalizes key and alias inputs", () => {
10+
expect(normalizeInteractiveChoice("1")).toBe("1");
11+
expect(normalizeInteractiveChoice("search")).toBe("1");
12+
expect(normalizeInteractiveChoice("Q")).toBe("0");
13+
});
14+
15+
test("rejects unknown choices", () => {
16+
expect(normalizeInteractiveChoice("")).toBe("");
17+
expect(normalizeInteractiveChoice("unknown")).toBe("");
18+
});
19+
20+
test("scores direct alias matches", () => {
21+
const search = INTERACTIVE_ACTIONS.find((action) => action.key === "1");
22+
expect(search).toBeDefined();
23+
if (!search) return;
24+
25+
const direct = scoreInteractiveAction(search, "search");
26+
const partial = scoreInteractiveAction(search, "sea");
27+
expect(direct).toBeGreaterThan(partial);
28+
});
29+
});

0 commit comments

Comments
 (0)