feat: Agent-Friendly MCP Layer 1 (v0.2.0)#4
Conversation
Version bump 0.1.12 -> 0.2.0, createRFQ baseChain/quoteChain cast (SDK type lag), README/smithery/llms-install/registry metadata refresh. Clean base for the agent-friendly Layer 1 work. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…for all inputs getHTLCStatus queries htlcStatus.initiatorHTLC, absent from the flat HTLCStatusResult schema, so GraphQL validation rejected EVERY call. Switch to getHTLCs (htlcs query, actually served). Replace the false-green test that mocked the rejected shape. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Heuristic classifier (UNAUTHORIZED/RATE_LIMITED/UPSTREAM_RPC_ERROR/RFQ_EXPIRED/NO_LIQUIDITY/TRADE_NOT_FOUND/VALIDATION_ERROR/UNKNOWN) with recovery hints; UNKNOWN never masks the original message. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…envelope Bare /50\d/ and /network/ misclassified validation errors (e.g. 'amount 500 invalid', 'unsupported network') as retryable UPSTREAM_RPC_ERROR. Scope to 5xx phrases/status-anchored numerics + specific network-failure phrases; validation phrases now fall through to VALIDATION_ERROR (non-retryable). Add negative + non-Error-throw tests. Document that the error envelope is intentionally structured content, not MCP isError. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…respond_rfq to template + pin tests Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ed assertions Old block only checked file-wide substring presence (any single USE WHEN passed all per-tool tests; >=6 floor pre-satisfied), so it could not catch a per-tool description regression. Replace with per-tool assertions anchored to text unique to each description, mirroring the create_rfq pin bar. Mutation-verified: removing a tool's DO NOT USE WHEN line now fails that tool's test. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Best-effort, same-process scope; prevents context-loss retries from double-triggering writes. Durable dedupe tracked as a backend issue. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cached the resolved value only, so two concurrent same client_request_id calls both missed the cache and both executed the write (MCP does not serialize tool requests). Cache the in-flight Promise and evict on rejection so op runs exactly once per key under concurrency while keeping failed writes retryable. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Holistic-review pre-publish fixes: (1) McpServer announced stale 0.1.12 while package.json is 0.2.0 — every client/registry would see the wrong version. (2) list_open_rfqs/list_my_trades page/pageSize descriptions promised 1-100 / 1-based but zod was z.number().optional() — tighten to .int().min(1).max(100) so an out-of-range arg fails fast with a clear schema error instead of an opaque downstream one. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (2)
📒 Files selected for processing (7)
📝 WalkthroughWalkthroughThis PR upgrades Hashlock MCP to version 0.2.0, introducing infrastructure for error classification, session-level idempotency, and standardized tool response handling. New utility libraries are created, integrated into tool handlers, and covered by comprehensive tests. Package metadata and documentation are updated to reflect the atomic-settlement positioning. ChangesVersion 0.2.0 Release: Error Handling, Idempotency & Tool Infrastructure
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/index.ts (1)
205-208:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winReject SUI RFQs that omit the chain.
The tool description says SUI legs require
baseChain/quoteChain, but the schema still acceptsbaseToken: "SUI"orquoteToken: "SUI"with no chain and only fails downstream. Add a local guard beforehl.createRFQ(...)so ambiguous SUI requests return a structured validation error immediately.🛡️ Suggested guard
wrapTool(async ({ baseToken, baseChain, quoteToken, quoteChain, side, amount, expiresIn, isBlind, client_request_id }) => { + if (baseToken.toUpperCase() === 'SUI' && !baseChain) { + throw new Error('baseChain is required when baseToken is SUI'); + } + if (quoteToken.toUpperCase() === 'SUI' && !quoteChain) { + throw new Error('quoteChain is required when quoteToken is SUI'); + } + // TODO: SDK type def (CreateRFQInput) lags backend — baseChain/quoteChain // are accepted by the GraphQL `createRFQ` mutation but not yet typed in // `@hashlock-tech/sdk`@0.2.0. Cast to bypass DTS build; remove once SDKAlso applies to: 215-221
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/index.ts` around lines 205 - 208, Add a pre-check before calling hl.createRFQ that rejects any RFQ where either baseToken or quoteToken equals "SUI" (case-insensitive) while the corresponding baseChain or quoteChain is missing/empty; detect tokens via baseToken/quoteToken and chains via baseChain/quoteChain, and return/throw a structured validation error (matching the existing Zod-style error shape) immediately instead of letting the request proceed to hl.createRFQ. Ensure the same guard is applied to the other hl.createRFQ invocation in this module so all ambiguous SUI RFQs fail fast with a clear validation message.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@server.json`:
- Line 4: The manifest's "version" value (currently "0.1.12" in server.json) is
out of sync with package.json's version ("0.2.0"); update the manifest version
to match package.json (or vice‑versa if package.json is wrong) so both version
fields are identical, and ensure the change is applied to the "version" key in
server.json and the package.json "version" token (and add a brief check in
release notes or CI to keep them in sync).
In `@src/index.ts`:
- Around line 29-30: The idempotency guard created by createIdempotencyGuard()
is currently keyed only by client_request_id, causing different write tools
(e.g., respond_rfq, create_htlc and the other four write functions referenced)
to share results; change the idempotency key composition to include the
tool/operation name and a stable payload fingerprint (hash of the serialized
arguments) so keys are namespaced per operation, and update the guard logic to
reject an attempt to reuse an existing key when the stored fingerprint differs
from the new payload (return an explicit mismatch/error rather than the cached
result); update all usages where idempotency is invoked (the variable
idempotency and calls around respond_rfq/create_htlc and the other four write
tools) and add a regression test that reuses the same client_request_id across
two different tools/argument sets to assert the second call is rejected or
flagged as a mismatch.
In `@src/lib/errors.ts`:
- Around line 32-33: The code currently sets message with String(err) which
loses message fields on plain object throwables; update the message extraction
in src/lib/errors.ts (the const message assignment and the similar occurrence
around lines 58-59) to: if err is an Error use err.message; else if err is an
object with a "message" property use that property (String((err as
any).message)); otherwise fall back to String(err). Modify the assignments near
the RULES loop and the second occurrence so both preserve object.message when
present.
In `@src/lib/idempotency.ts`:
- Around line 14-25: The idempotency Map named "cache" used by remember(key, op)
never evicts successful entries causing unbounded memory growth; replace it with
a bounded cache (either integrate an LRU with max-size or add TTL+LRU logic) so
entries expire: implement a capped LRU (or use a library like lru-cache) keyed
by the same key string and store the Promise values, or add a timestamp per
entry and evict stale entries on access and enforce a max size by removing the
least-recently-used item when inserting; ensure failed ops still delete the key
(keep the existing catch that calls cache.delete(key)) and update references
from the Map to the new cache implementation in remember.
---
Outside diff comments:
In `@src/index.ts`:
- Around line 205-208: Add a pre-check before calling hl.createRFQ that rejects
any RFQ where either baseToken or quoteToken equals "SUI" (case-insensitive)
while the corresponding baseChain or quoteChain is missing/empty; detect tokens
via baseToken/quoteToken and chains via baseChain/quoteChain, and return/throw a
structured validation error (matching the existing Zod-style error shape)
immediately instead of letting the request proceed to hl.createRFQ. Ensure the
same guard is applied to the other hl.createRFQ invocation in this module so all
ambiguous SUI RFQs fail fast with a clear validation message.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: a450cac1-faea-48ac-a46a-90219b1d0230
📒 Files selected for processing (16)
.gitignoreREADME.mdllms-install.mdpackage.jsonserver.jsonsmithery.yamlsrc/__tests__/errors.test.tssrc/__tests__/idempotency.test.tssrc/__tests__/pairs.test.tssrc/__tests__/result.test.tssrc/__tests__/tools.test.tssrc/index.tssrc/lib/errors.tssrc/lib/idempotency.tssrc/lib/pairs.tssrc/lib/result.ts
CI: package.json had @hashlock-tech/sdk ^0.2.0 (v0.2.0 sweep) but lockfile + all tests/build ran against 0.1.x — pin to ^0.1.4 (the verified version) and regenerate lockfile; SDK 0.2.0 upgrade is a separate tested change. CodeRabbit: server.json version 0.1.12→0.2.0 (registry-publish blocker); errors.ts preserves .message for non-Error throwables (was [object Object]); idempotency key now scoped by tool+payload so a reused client_request_id can't replay an unrelated result; bounded the idempotency Map (FIFO cap 1000) to prevent unbounded growth. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…h guard Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Re the outside-diff comment on src/index.ts ~205-208 (reject SUI RFQs that omit the chain): deferred deliberately, not ignored. |
Agent-Friendly MCP — Layer 1 (v0.2.0)
Hardens the Hashlock MCP server for autonomous-agent use. Spec + plan:
Hashlock-Tech/hashlock-marketsdocs/superpowers/{specs,plans}/2026-05-18-agent-friendly-layer1*.What shipped
get_htlc(cca80a2): switched the brokengetHTLCStatusquery togetHTLCs(thehtlcsquery the backend actually serves).getHTLCStatusrequestedhtlcStatus.initiatorHTLC, a field absent from the flatHTLCStatusResultschema, so GraphQL validation rejected every call — the only read tool had never worked against the deployed backend. Replaced the false-green test (it mocked the rejected shape) with real array-shape + empty-array tests.4c93e4e,a27f8d9): every tool now returns{ error: { code, message, is_retryable, recovery_hint, details } }as normal content on failure (8 classified codes;UNKNOWNnever masks the original message). Deliberately not an MCPisErrorprotocol fault — documented intoErrorEnvelope.b058f2b,f139fe8): optionalclient_request_idon the 5 write tools; a context-losing agent retrying the same write within a session replays the first result instead of double-submitting on-chain. Concurrent same-key calls dedup (in-flight Promise cache); failed writes stay retryable. Best-effort/session-scoped; durable backend dedupe tracked inhashlock-markets#355.d960d97,f5fa561): read-onlylist_supported_pairs,list_open_rfqs,list_my_trades— lets an agent rebuild state after context loss and discover markets without hardcoding.76e3019,b3ed9a9):create_htlc/withdraw_htlc/refund_htlc/respond_rfqbrought to theUSE WHEN / DO NOT USE WHEN / PARAM NOTEStemplate; per-tool pin tests (mutation-verified) guard the LLM-routing markers;create_rfqintent-compiler untouched.6f0307a): pureokContenthelper so handler shape is unit-testable.24c514d) + version sync / page-bound enforcement (0d27854).Test plan
npm run lint(tsc --noEmit) — cleannpm test— 65/65 pass (5 files)npm run build(tsup) — emitsdist/index.js+dist/index.d.tsorigin/main, then npm + MCP-registry publish (operator action — intentionally not done here)🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation