Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
92cec4b
docs(claude): require feature branches for all Linear-tracked work
Gastonfoncea May 18, 2026
3d95f9a
docs(specs): MCP Sprint B on-chain tools design — GHB-187
Gastonfoncea May 18, 2026
1703e52
docs(plans): MCP Sprint B implementation plan — GHB-187
Gastonfoncea May 18, 2026
b2845ff
feat(db): add agent_delegations table — GHB-187
Gastonfoncea May 18, 2026
adb4c04
feat(shared): add verifyPrOwnership helper — GHB-187
Gastonfoncea May 18, 2026
0a5948c
chore(mcp): add @privy-io/node dependency — GHB-187
Gastonfoncea May 18, 2026
88593c9
docs(plans): flag db:migrate as explicit Task 16 step — GHB-187
Gastonfoncea May 18, 2026
3bfeb04
feat(mcp): add requireRole guard — GHB-187
Gastonfoncea May 18, 2026
332f72d
feat(mcp): add requireWalletDelegated guard — GHB-187
Gastonfoncea May 18, 2026
d6839a0
feat(mcp): add Privy delegated-signer wrapper — GHB-187
Gastonfoncea May 18, 2026
c1f5ffa
feat(mcp): add server-side submit_solution tx builder — GHB-187
Gastonfoncea May 18, 2026
ff02667
feat(mcp): add submissions.list tool — GHB-187
Gastonfoncea May 18, 2026
8848455
feat(mcp): add submissions.create (submit_pr) happy path — GHB-187
Gastonfoncea May 18, 2026
e557c47
test(mcp): cover remaining error cases for submissions.create — GHB-187
Gastonfoncea May 18, 2026
d865d74
feat(frontend): add agent-delegation API route — GHB-187
Gastonfoncea May 19, 2026
5627e7d
feat(frontend): add AgentDelegationCard consent UI — GHB-187
Gastonfoncea May 19, 2026
4e1a95e
feat(frontend): render AgentDelegationCard in /app/credentials — GHB-187
Gastonfoncea May 19, 2026
bacb4c3
feat(relayer): pre-scoring PR ownership check — GHB-187
Gastonfoncea May 19, 2026
fea5786
docs(runbooks): add Sprint B smoke test runbook — GHB-187
Gastonfoncea May 19, 2026
2671b1d
fix(mcp): resolve Privy walletId via getWalletByAddress — GHB-187
Gastonfoncea May 19, 2026
d8410a3
fix(shared,relayer): keep extensionless imports for Next/Turbopack — …
Gastonfoncea May 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,16 @@ This is a **pnpm workspace**, not npm. Use `pnpm` and `pnpm --filter @ghbounty/<
## Linear issues

Issues are tracked in Linear. The repo branch naming convention is `<github-handle>/<issue-slug>` (e.g. `gastonfoncea09/ghb-188-mcp-frontend-onboarding`). Commit messages should reference the Linear issue (`— GHB-188`).

### Linear-tracked work always goes on its own branch

**Never commit directly to `main`** (or any base branch) when implementing a Linear issue. This applies to **everything** tied to the issue: spec docs, implementation plans, code, migrations, tests, runbooks. All of it lives on the feature branch and reaches `main` only via a merged PR.

Workflow when starting a Linear issue:

1. Move the Linear issue to **In Progress** (and re-assign to yourself if needed).
2. Create the feature branch using the Linear-provided `gitBranchName` (Linear shows it on the issue page). `git checkout -b <branch>`.
3. All commits for the issue go on that branch — including the spec/plan docs in `docs/superpowers/`.
4. Open a PR when ready. Merge only after review + CI green.

The only commits that may land on `main` directly are repo-wide chores not tied to a Linear issue (workflow docs, root README typos, etc.) — and even then, prefer a branch + PR when in doubt.
1 change: 1 addition & 0 deletions apps/mcp/lib/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export type McpErrorCode =
| "NotFound"
| "Conflict"
| "RpcError"
| "ServiceUnavailable"
| "InternalError"
| "InvalidInput";

Expand Down
62 changes: 62 additions & 0 deletions apps/mcp/lib/gas-station/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* Server-side SolanaGasStation singleton for the MCP app.
*
* Rather than going through the frontend's HTTP endpoint at
* /api/gas-station/sponsor, the MCP app uses the SolanaGasStation class
* directly — no extra network hop, no service-to-service auth token needed,
* and full control over error handling.
*
* Wired in GHB-187 (submissions.create / submit_pr).
*/

import { Connection } from "@solana/web3.js";
import {
SolanaGasStation,
GasStationError,
loadGasStationKeypair,
makeConnectionRpcSubmitter,
} from "@ghbounty/shared";
import type { ChainId } from "@ghbounty/shared";

let cached: SolanaGasStation | null = null;

function get(): SolanaGasStation {
if (cached) return cached;

const rpcUrl = process.env.SOLANA_RPC_URL;
if (!rpcUrl) throw new Error("SOLANA_RPC_URL must be set");

const chainId = (process.env.CHAIN_ID ?? "solana-devnet") as ChainId;

cached = new SolanaGasStation({
chainId,
keypair: loadGasStationKeypair(),
rpc: makeConnectionRpcSubmitter(new Connection(rpcUrl, "confirmed")),
});
return cached;
}

export async function submitSponsoredTx(
signedTxBytes: Uint8Array
): Promise<{ ok: true; signature: string } | { ok: false; reason: string }> {
const gasStation = get();
const chainId = (process.env.CHAIN_ID ?? "solana-devnet") as ChainId;
try {
const result = await gasStation.sponsor({
chainId,
payload: {
kind: "solana",
partiallySignedTxB64: Buffer.from(signedTxBytes).toString("base64"),
},
});
return { ok: true, signature: result.txHash };
} catch (err: unknown) {
// Return only safe, structured codes — never raw error messages that may
// contain internal details (pubkeys, discriminator codes, RPC responses).
const reason =
err instanceof GasStationError
? err.code // "validator_rejected" | "rpc_error" | "unsupported_chain" — safe
: "unexpected_error";
return { ok: false, reason };
}
}
Loading
Loading