Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

Kotlin/Java: Retrofit client interfaces are minted as server `Route` nodes instead of outbound `HTTP_CALLS`

未关闭
#2,723 1 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 2 天内回复

还没有人认领这个 Issue。

评估

难度
4/5
预计耗时
3-5 天
新手友好度
57/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
java, kotlin
领域
devtools

调研方向

Start by reading the Kotlin and Java HTTP indexing paths that create Route nodes and HTTP_CALLS edges, then compare their handling of Retrofit annotations with the existing JAX-RS and RestTemplate behavior described here. Reproduce with the supplied OrderApi.kt and Java interface examples, checking both graph edge types and route paths. Done when Retrofit methods produce the expected outbound calls and paths, with no client HANDLES routes or cross-repository matches landing on the client.

由索引模型根据 Issue 内容生成。

描述

bug parsing/quality ux/behavior
Version

codebase-memory-mcp 0.12.1

Platform

Linux (x64)

Install channel

GitHub release archive / install.sh / install.ps1

Binary variant

ui

What happened, and what did you expect?

A Retrofit service interface (@GET/@POST/... from retrofit2.http, in Kotlin or Java) declares the HTTP calls
a client makes, but it is indexed as if the client served them: each annotated method becomes a Route
node of the client project with a HANDLES edge from the interface method. Three consequences:

  • Outbound calls lost. The interface methods get no HTTP_CALLS edge, so cross-repo-intelligence reports
    cross_http_calls: 0 for a client whose whole HTTP surface is Retrofit, which is the usual shape of an
    Android app.
  • Other callers get linked to the client. A path written with a leading slash (@GET("/v1/orders/{id}"),
    which Retrofit treats as absolute: "Endpoint values which contain a leading / are absolute",
    Retrofit.java:581)
    yields a Route identical to the server's real one. cross-repo-intelligence from any other project that
    calls the same endpoint then emits a CROSS_HTTP_CALLS pair into the client as well as into the server, as if
    the client's interface method handled the request.
  • Paths collapse onto /. Any path that is not a string literal with a leading slash becomes /, so all
    such methods collide on __route__GET__/ / __route__POST__/:
    • the idiomatic relative path (@POST("v1/orders")), resolved at runtime against the baseUrl
      (Retrofit.java:560);
    • a Kotlin const val, alone (@GET(ORDERS)) or in a string template (@GET("$ORDERS/{id}/status")), even
      when the constant itself starts with /. Retrofit annotation arguments must be compile-time constants, so
      this is a common way to share a path prefix across the methods of one interface.

Expected: see "Result vs expected" in the Reproduction section.

Reproduction

1. Code. Three dummy repos, created as /tmp/cbm-kotlin-retrofit-client, /tmp/cbm-orders-server and
/tmp/cbm-billing-caller.

Client repo /tmp/cbm-kotlin-retrofit-client, under src/main/kotlin/com/acme/client/:

// OrderApi.kt   (Retrofit, Kotlin coroutines)
package com.acme.client

import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Path

private const val ORDERS = "/v1/orders"

interface OrderApi {
    @GET("/v1/orders/{id}")
    suspend fun getOrder(@Path("id") id: String): String

    @POST("v1/orders")
    suspend fun createOrder(@Body body: String): String

    @GET(ORDERS)
    suspend fun listOrders(): String

    @GET("$ORDERS/{id}/status")
    suspend fun getOrderStatus(@Path("id") id: String): String
}

Server repo /tmp/cbm-orders-server, under src/main/java/com/acme/server/ (JAX-RS; indexes correctly):

// OrderResource.java
package com.acme.server;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
@Path("/v1/orders")
public class OrderResource {
    @GET public String list() { return "[]"; }
    @GET @Path("/{id}") public String get(@PathParam("id") String id) { return id; }
    @GET @Path("/{id}/status") public String status(@PathParam("id") String id) { return id; }
    @POST public String create(String body) { return body; }
}

Another caller of the same server, /tmp/cbm-billing-caller, under src/main/java/com/acme/billing/ (an inline
RestTemplate, a shape that already produces HTTP_CALLS):

// BillingJob.java
package com.acme.billing;
import org.springframework.web.client.RestTemplate;
public class BillingJob {
    public String order(String id) {
        return new RestTemplate().getForObject("/v1/orders/{id}", String.class, id);
    }
}

2. Commands. The project ids below are what the indexer derives from the /tmp/... paths.

codebase-memory-mcp cli index_repository '{"repo_path":"/tmp/cbm-kotlin-retrofit-client","mode":"moderate"}'
codebase-memory-mcp cli index_repository '{"repo_path":"/tmp/cbm-orders-server","mode":"moderate"}'
codebase-memory-mcp cli index_repository '{"repo_path":"/tmp/cbm-billing-caller","mode":"moderate"}'
codebase-memory-mcp cli query_graph '{"project":"tmp-cbm-kotlin-retrofit-client","query":"MATCH (f)-[e:HANDLES]->(r:Route) RETURN f.file_path, f.name, r.qualified_name"}'
codebase-memory-mcp cli query_graph '{"project":"tmp-cbm-kotlin-retrofit-client","query":"MATCH (f)-[e:HTTP_CALLS]->(r) RETURN f.name, r.qualified_name"}'
codebase-memory-mcp cli query_graph '{"project":"tmp-cbm-orders-server","query":"MATCH (r:Route) RETURN r.qualified_name"}'
codebase-memory-mcp cli index_repository '{"repo_path":"/tmp/cbm-kotlin-retrofit-client","mode":"cross-repo-intelligence","target_projects":["tmp-cbm-orders-server"]}'
codebase-memory-mcp cli index_repository '{"repo_path":"/tmp/cbm-billing-caller","mode":"cross-repo-intelligence","target_projects":["tmp-cbm-kotlin-retrofit-client","tmp-cbm-orders-server"]}'
codebase-memory-mcp cli query_graph '{"project":"tmp-cbm-kotlin-retrofit-client","query":"MATCH (h)-[e:CROSS_HTTP_CALLS]->(r) RETURN h.file_path, h.name, r.qualified_name"}'
codebase-memory-mcp cli query_graph '{"project":"tmp-cbm-orders-server","query":"MATCH (h)-[e:CROSS_HTTP_CALLS]->(r) RETURN h.file_path, h.name, r.qualified_name"}'

3. Result vs expected. CLI output, trimmed to the rows.

Client HANDLES edges (expected: 0 rows; only the leading-slash literal keeps its path, the relative path and
both const val forms collapse to /):

rows: 4  (cols: f.file_path f.name r.qualified_name)
  src/main/kotlin/com/acme/client/OrderApi.kt getOrder __route__GET__/v1/orders/{}
  src/main/kotlin/com/acme/client/OrderApi.kt createOrder __route__POST__/
  src/main/kotlin/com/acme/client/OrderApi.kt listOrders __route__GET__/
  src/main/kotlin/com/acme/client/OrderApi.kt getOrderStatus __route__GET__/

Client HTTP_CALLS edges:

rows: 0  (cols: f.name r.qualified_name)

Expected 4 rows:

  getOrder       __route__GET__/v1/orders/{}
  createOrder    __route__POST__/v1/orders
  listOrders     __route__GET__/v1/orders
  getOrderStatus __route__GET__/v1/orders/{}/status

Server Route nodes (correct):

rows: 4  (cols: r.qualified_name)
  __route__GET__/v1/orders
  __route__GET__/v1/orders/{}
  __route__GET__/v1/orders/{}/status
  __route__POST__/v1/orders

cross-repo-intelligence from the client: "cross_http_calls":0. Expected 4.

cross-repo-intelligence from the billing caller: "cross_http_calls":2 for its single call. Expected 1. The
reverse edges show the second one landing on the client's interface method:

# tmp-cbm-kotlin-retrofit-client (expected: 0 rows)
rows: 1  (cols: h.file_path h.name r.qualified_name)
  src/main/kotlin/com/acme/client/OrderApi.kt getOrder __route__GET__/v1/orders/{}

# tmp-cbm-orders-server (correct)
rows: 1  (cols: h.file_path h.name r.qualified_name)
  src/main/java/com/acme/server/OrderResource.java get __route__GET__/v1/orders/{}

The same holds for a Java Retrofit interface: @GET("v1/orders/{id}") and @POST("v1/orders") on a Java
interface give HANDLES to __route__GET__/ and __route__POST__/ and no HTTP_CALLS.

Related: #2668 (Java outbound calls through injected clients, format strings and request builders yield no
HTTP_CALLS; same "outbound call lost" family, different mechanism), #1248 (false Routes minted from
non-registration string literals).

Logs

Diagnostics trajectory (memory / performance / leak issues)

Project scale (if relevant)

Impact on a real codebase. On a production Kotlin Android app (29 Retrofit interfaces, 85 annotated
methods; proprietary, so only counts here):

  • 85 HANDLES edges onto 67 client Route nodes, 0 HTTP_CALLS;
  • 16 methods (relative paths and const val templates) collapse onto __route__GET__/ and
    __route__POST__/;
  • of the 65 other distinct paths, 55 match a HANDLES-backed Route of the backend services the app talks to,
    so classifying them as outbound calls would give the app 55 correct cross-repo links instead of 0 (the
    remaining 10 are static files and third-party APIs);
  • 15 CROSS_HTTP_CALLS from call sites in 6 other repositories of the same fleet land on the app's interface
    methods.
Confirmations
  • I searched existing issues and this is not a duplicate.
  • My reproduction uses shareable code (a dummy snippet or a public OSS repository), not proprietary code.
主要语言
C
星标
45.8k
派生
3.8k
平均合并
3 天 6 小时
30 天内合并 PR
201

环境准备

这个项目没有提供开发容器、Dockerfile 或贡献指南,环境需要你自己搭建:先看它的 README,通用步骤见我们的新手贡献指南。

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

DeusData/codebase-memory-mcp 的其他 Issue

查看 DeusData/codebase-memory-mcp 的全部 Issue

相似的 Issue

更多 C Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。