[DOJ-2175/2177/2178] feat: rewrite MarketplaceService with real API calls#6
[DOJ-2175/2177/2178] feat: rewrite MarketplaceService with real API calls#6dbejarano820 wants to merge 4 commits into
Conversation
Add apiKey field and setApiKey() method to MarketplaceService so the HTTP transport can inject the caller's Bearer token per request. Remove the apiKey parameter from install() — all methods now use the instance field, falling back to DOJO_API_KEY env var at construction. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add private callApi() method that POSTs to the marketplace-mcp-api Edge Function. Replace search, listCategories, getDetails, getItemDetail, uninstall, and listInstalled stubs with real callApi() invocations. Private helpers (resolveSlug, getVersionRecord, incrementDownloadCount) now use this.apiKey instead of parameter threading. Update tests to mock fetch for all API-calling methods. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Greptile SummaryReplaces MarketplaceService stub methods with real Edge Function API calls via a new Critical issue: DOJ-2177's per-request API key injection is incomplete.
Confidence Score: 1/5
|
| Filename | Overview |
|---|---|
| src/transports/http.ts | Critical bug: per-request API key stored but never used, defeating the race condition fix |
| src/services/marketplace.service.ts | Replaces stub methods with real API calls, adds MethodOptions parameter, adds timeouts and empty string guards |
| src/index.ts | Passes service parameter to startHttpServer for per-request API key injection |
Sequence Diagram
sequenceDiagram
participant Client
participant HTTP as HTTP Middleware
participant Service as MarketplaceService
participant API as Edge Function
Client->>HTTP: POST /mcp + Authorization header
HTTP->>HTTP: Extract & validate API key
HTTP->>HTTP: Store in res.locals.dojoApiKey
Note over HTTP,Service: ❌ API key NOT passed to service
HTTP->>Service: Call tool handler
Service->>Service: Use this.apiKey (default/env)
Service->>API: POST with wrong API key
API-->>Service: Response
Service-->>HTTP: Result
HTTP-->>Client: Response
Last reviewed commit: a4f85da
|
|
||
| return data.map((slug) => { | ||
| const display = CATEGORY_DISPLAY[slug] ?? { | ||
| name: slug.charAt(0).toUpperCase() + slug.slice(1), |
There was a problem hiding this comment.
Empty string handling - if slug is an empty string, .charAt(0) returns empty string and capitalization fails silently
| name: slug.charAt(0).toUpperCase() + slug.slice(1), | |
| name: slug ? slug.charAt(0).toUpperCase() + slug.slice(1) : "Unknown", |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/services/marketplace.service.ts
Line: 244
Comment:
Empty string handling - if `slug` is an empty string, `.charAt(0)` returns empty string and capitalization fails silently
```suggestion
name: slug ? slug.charAt(0).toUpperCase() + slug.slice(1) : "Unknown",
```
How can I resolve this? If you propose a fix, please make it concise.- Add per-call apiKey parameter to all public MarketplaceService methods to prevent API key leakage between concurrent HTTP requests - Add AbortSignal.timeout(30s) to resolveSlug, getVersionRecord, and incrementDownloadCount fetch calls - Add empty string guard in listCategories slug mapping [DOJ-2175] Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace `declare global { namespace Express }` with `res.locals`
to satisfy @typescript-eslint/no-namespace ESLint rule.
[DOJ-2175]
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@greptile review again |
| export async function startHttpServer(server: McpServer): Promise<void> { | ||
| export async function startHttpServer( | ||
| server: McpServer, | ||
| _service: MarketplaceService, |
There was a problem hiding this comment.
_service parameter is marked as unused but the race condition fix is incomplete
The underscore prefix indicates this parameter is intentionally unused, but DOJ-2177's goal was to inject the per-request API key. Since tool handlers don't access res.locals.dojoApiKey, calling _service.setApiKey(authCtx.apiKey) here would fix the race condition (though still has timing issues if multiple tools are called in parallel within one request).
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/transports/http.ts
Line: 11
Comment:
`_service` parameter is marked as unused but the race condition fix is incomplete
The underscore prefix indicates this parameter is intentionally unused, but DOJ-2177's goal was to inject the per-request API key. Since tool handlers don't access `res.locals.dojoApiKey`, calling `_service.setApiKey(authCtx.apiKey)` here would fix the race condition (though still has timing issues if multiple tools are called in parallel within one request).
How can I resolve this? If you propose a fix, please make it concise.| return; | ||
| } | ||
| // Store on res.locals — no shared-state mutation, no type augmentation | ||
| res.locals.dojoApiKey = authCtx.apiKey; |
There was a problem hiding this comment.
stored value is never used — tool handlers don't pass it to service methods
The value stored on line 27 is not retrieved or passed to MarketplaceService methods. Tool handlers in browse.tools.ts and install.tools.ts call service methods like service.search(args) without passing the per-request value via options, so they'll use the default instance field instead.
Fix: call _service.setApiKey(authCtx.apiKey) after line 27 to inject the per-request value into the service instance.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/transports/http.ts
Line: 27
Comment:
stored value is never used — tool handlers don't pass it to service methods
The value stored on line 27 is not retrieved or passed to `MarketplaceService` methods. Tool handlers in `browse.tools.ts` and `install.tools.ts` call service methods like `service.search(args)` without passing the per-request value via options, so they'll use the default instance field instead.
Fix: call `_service.setApiKey(authCtx.apiKey)` after line 27 to inject the per-request value into the service instance.
How can I resolve this? If you propose a fix, please make it concise.|
|
||
| return data.map((slug) => { | ||
| const display = CATEGORY_DISPLAY[slug] ?? { | ||
| name: slug ? slug.charAt(0).toUpperCase() + slug.slice(1) : "Unknown", |
There was a problem hiding this comment.
empty string guard added (already noted in previous review thread)
The ternary slug ? ... : "Unknown" handles the empty string case mentioned in the previous review. Consider whether "Unknown" is the right fallback or if empty strings should be filtered from the API response entirely.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/services/marketplace.service.ts
Line: 254
Comment:
empty string guard added (already noted in previous review thread)
The ternary `slug ? ... : "Unknown"` handles the empty string case mentioned in the previous review. Consider whether "Unknown" is the right fallback or if empty strings should be filtered from the API response entirely.
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise.| status: response.status, | ||
| statusText: response.statusText, | ||
| }); | ||
| const error = await response.json().catch(() => ({})); |
There was a problem hiding this comment.
error parsing assumes JSON — will throw if Edge Function returns HTML or plain text on error
.json().catch(() => ({})) falls back to empty object, so the error message becomes generic API error: {status} instead of showing the actual error text. Consider .text() fallback for better error messages.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/services/marketplace.service.ts
Line: 200
Comment:
error parsing assumes JSON — will throw if Edge Function returns HTML or plain text on error
`.json().catch(() => ({}))` falls back to empty object, so the error message becomes generic `API error: {status}` instead of showing the actual error text. Consider `.text()` fallback for better error messages.
How can I resolve this? If you propose a fix, please make it concise.|
🤖 This PR has had no activity for 3 days. If it's still needed, please:
If it's no longer needed, please close it. — Dojito (automated PR gardener) |
|
@greptileai what is missing for this to be a 5/5 PR? |
|
Looking at the PR and review comments, here's what's missing for 5/5: Critical Issue: Fix: Call Secondary Issues:
Test Plan:
Once the API key wiring is complete and those items are verified, this will be a solid 5/5. |
|
🤖 This PR has had no activity for 7 days. If it's still needed, please:
If it's no longer needed, please close it. — Dojito (automated PR gardener) |
|
🤖 This PR has had no activity for 10 days. If it's still needed, please:
If it's no longer needed, please close it. — Dojito (automated PR gardener) |
|
🤖 This PR has had no activity for 15 days. If it's still needed, please:
If it's no longer needed, please close it. — Dojito (automated PR gardener) |
1 similar comment
|
🤖 This PR has had no activity for 15 days. If it's still needed, please:
If it's no longer needed, please close it. — Dojito (automated PR gardener) |
|
🤖 This PR has had no activity for 3 days. If it's still needed, please:
If it's no longer needed, please close it. — Dojito (automated PR gardener) |
|
🤖 This PR has had no activity for 3 days. If it's still needed, please:
If it's no longer needed, please close it. — Dojito (automated PR gardener) |
|
🤖 This PR has had no activity for 24+ days. If it's still needed, please:
If it's no longer needed, please close it. — Dojito (automated PR gardener) |
Summary
setApiKey(). RemovesapiKeyparameter threading frominstall()and private helpers — all methods now use the instance field.callApi()method that POSTs to themarketplace-mcp-apiEdge Function. Replaces all stub service methods (search, listCategories, getDetails, getItemDetail, uninstall, listInstalled) with real API calls. UpdatesMarketplaceCategorytype to{ name, slug, description }.marketplace_list_categorieswith real data — maps Edge Function's['skill', 'plugin', 'tool']response to full category objects with display names and descriptions.Linear
Closes DOJ-2175, DOJ-2177, DOJ-2178
Test plan
npm testpasses (24 tests)npm run typecheckpassesnpm run buildsucceeds🤖 Generated with Claude Code