ProtoMap benchmark

ProtoMap makes Claude Code 6.4× faster and 5.3× cheaper across 9 gRPC repositories with 5 million lines of code

We asked Claude Code 20 questions engineers ask every day in gRPC and protobuf codebases, on nine repositories in seven languages with 2,380 gRPC services, with and without the ProtoMap MCP server, twice each, with Opus 5.5 at high effort and Sonnet 5.5 at extra-high effort. Answers were graded by code against an answer key that does not come from ProtoMap. October 28, 2026.

6.4×
faster per prompt (geometric mean over prompts and both models)
5.3×
cheaper per prompt, at the same accuracy

Opus 5.5 · high effort

Faster, per prompt
7.7×
geometric mean; 96m 55s → 10m 42s for all 20
Cheaper, per prompt
6.4×
geometric mean; $50.69 → $5.13 for all 20
Fewer tokens, per prompt
13×
80.5M → 3.4M for all 20
Accuracy
100%
with ProtoMap, against 98% without; 0 baseline runs hit the 45-minute limit

Sonnet 5.5 · xhigh effort

Faster, per prompt
5.3×
geometric mean; 82m 34s → 12m 15s for all 20
Cheaper, per prompt
4.4×
geometric mean; $22.05 → $3.68 for all 20
Fewer tokens, per prompt
8.3×
64.3M → 4.2M for all 20
Accuracy
100%
with ProtoMap, against 98% without; 0 baseline runs hit the 45-minute limit

Side by side

Two live Claude Code sessions (Opus 5.5, high effort) given Q09 at the same moment, recorded on one screen: ProtoMap's answers in 50 seconds for $0.40, the baseline's in 6 minutes for $14.44. Sped up where marked.

Open the video page →  ·  Want the full story? Watch the live ProtoMap demo at MozFest →

Per prompt

Claude Code alone (baseline) Claude Code with ProtoMap MCP

Opus 5.5 · high effort: time to answer

Median wall-clock time of 2 runs per prompt (their mean), log scale. The figure at the right of each row is how many times faster the answer came with ProtoMap.

3s 10s 30s 1m 3m 10m 30m 60m Q01 · list services 1.8× Q02 · most connected 121× Q03 · service dependents 22× Q04 · rpc callers 8.3× Q05 · field blast radius 18× Q06 · request field change 3.0× Q07 · response provenance 1.4× Q08 · two hop lineage 2.0× Q09 · remove rpc 18× Q10 · outbound dependencies 2.4× Q11 · transitive impact 28× Q12 · call chain 39× Q13 · non handler callers 11× Q14 · api docs 5.3× Q15 · dead rpcs 1.1× Q16 · pii flow 15× Q17 · response consumers 11× Q18 · handler fanout 4.2× Q19 · domain dependencies 8.7× Q20 · proto change repos 5.4×

Opus 5.5 · high effort: cost per answer

Median cost of 2 runs per prompt (their mean) at API prices, as Claude Code reports it, log scale.

$0.010 $0.10 $1.00 $10.00 Q01 · list services 1.9× Q02 · most connected 64× Q03 · service dependents 9.8× Q04 · rpc callers 6.0× Q05 · field blast radius 41× Q06 · request field change 2.6× Q07 · response provenance 1.2× Q08 · two hop lineage 2.4× Q09 · remove rpc 23× Q10 · outbound dependencies 2.7× Q11 · transitive impact 15× Q12 · call chain 20× Q13 · non handler callers 44× Q14 · api docs 4.6× Q15 · dead rpcs 1.9× Q16 · pii flow 4.8× Q17 · response consumers 8.1× Q18 · handler fanout 0.9× Q19 · domain dependencies 4.8× Q20 · proto change repos 4.9×
Opus 5.5 · high effort: table view
PromptBaseline timeProtoMap timeFasterBaseline costProtoMap costCheaperBaseline tokensProtoMap tokensProtoMap calls
Q01 · list services1m 12s39s1.8×$0.58$0.311.9×411K109K3
Q02 · most connected23m 56s12s121×$9.86$0.1564×15.8M71K2
Q03 · service dependents6m 36s18s22×$1.81$0.199.8×2.7M73K2
Q04 · rpc callers3m 19s24s8.3×$1.13$0.196.0×1.2M98K1
Q05 · field blast radius6m 27s21s18×$9.00$0.2241×16.8M107K4
Q06 · request field change3m 24s1m 09s3.0×$1.69$0.652.6×1.9M693K4
Q07 · response provenance25s18s1.4×$0.24$0.211.2×172K161K1
Q08 · two hop lineage49s24s2.0×$0.47$0.202.4×378K121K5
Q09 · remove rpc8m 13s27s18×$4.46$0.2023×7.0M130K2
Q10 · outbound dependencies53s22s2.4×$0.49$0.192.7×473K84K6
Q11 · transitive impact7m 18s16s28×$1.97$0.1315×3.4M68K2
Q12 · call chain5m 09s8s39×$1.55$0.07620×2.0M62K1
Q13 · non handler callers4m 59s28s11×$8.70$0.2044×17.1M100K1
Q14 · api docs1m 54s22s5.3×$1.06$0.234.6×1.3M127K3
Q15 · dead rpcs2m 49s2m 41s1.1×$0.97$0.511.9×1.1M502K4
Q16 · pii flow7m 12s29s15×$1.45$0.304.8×2.4M178K9
Q17 · response consumers4m 43s25s11×$2.48$0.318.1×3.5M164K4
Q18 · handler fanout2m 32s36s4.2×$0.34$0.390.9×261K372K2
Q19 · domain dependencies2m 54s20s8.7×$0.92$0.194.8×1.0M86K2
Q20 · proto change repos2m 11s24s5.4×$1.51$0.314.9×1.6M139K8
All 20 prompts96m 55s10m 42s9.1×$50.69$5.139.9×80.5M3.4M

Sonnet 5.5 · xhigh effort: time to answer

Median wall-clock time of 2 runs per prompt (their mean), log scale. The figure at the right of each row is how many times faster the answer came with ProtoMap.

10s 30s 1m 3m 10m 30m 60m Q01 · list services 2.1× Q02 · most connected 23× Q03 · service dependents 3.3× Q04 · rpc callers 7.2× Q05 · field blast radius 6.7× Q06 · request field change 1.9× Q07 · response provenance 1.5× Q08 · two hop lineage 4.3× Q09 · remove rpc 3.1× Q10 · outbound dependencies 1.9× Q11 · transitive impact 23× Q12 · call chain 27× Q13 · non handler callers 18× Q14 · api docs 7.1× Q15 · dead rpcs 4.2× Q16 · pii flow 5.1× Q17 · response consumers 9.8× Q18 · handler fanout 1.2× Q19 · domain dependencies 11× Q20 · proto change repos 2.5×

Sonnet 5.5 · xhigh effort: cost per answer

Median cost of 2 runs per prompt (their mean) at API prices, as Claude Code reports it, log scale.

$0.010 $0.10 $1.00 $10.00 Q01 · list services 2.2× Q02 · most connected 19× Q03 · service dependents 3.7× Q04 · rpc callers 5.2× Q05 · field blast radius 8.5× Q06 · request field change 1.9× Q07 · response provenance 1.1× Q08 · two hop lineage 1.7× Q09 · remove rpc 6.3× Q10 · outbound dependencies 2.0× Q11 · transitive impact 16× Q12 · call chain 18× Q13 · non handler callers 32× Q14 · api docs 4.6× Q15 · dead rpcs 2.0× Q16 · pii flow 3.8× Q17 · response consumers 6.8× Q18 · handler fanout 0.7× Q19 · domain dependencies 9.0× Q20 · proto change repos 2.1×
Sonnet 5.5 · xhigh effort: table view
PromptBaseline timeProtoMap timeFasterBaseline costProtoMap costCheaperBaseline tokensProtoMap tokensProtoMap calls
Q01 · list services1m 29s42s2.1×$0.46$0.202.2×781K175K4
Q02 · most connected21m 57s57s23×$5.03$0.2719×22.6M210K12
Q03 · service dependents1m 39s30s3.3×$0.44$0.123.7×686K123K2
Q04 · rpc callers2m 11s18s7.2×$0.40$0.0775.2×663K67K1
Q05 · field blast radius4m 26s40s6.7×$1.41$0.178.5×4.5M208K4
Q06 · request field change1m 46s57s1.9×$0.57$0.301.9×908K399K4
Q07 · response provenance28s19s1.5×$0.16$0.151.1×190K146K5
Q08 · two hop lineage2m 29s35s4.3×$0.41$0.241.7×656K293K8
Q09 · remove rpc6m 24s2m 04s3.1×$1.98$0.316.3×7.2M397K2
Q10 · outbound dependencies1m 05s34s1.9×$0.34$0.172.0×543K175K16
Q11 · transitive impact5m 34s14s23×$1.01$0.06416×1.9M67K1
Q12 · call chain7m 04s16s27×$1.22$0.06818×2.8M133K1
Q13 · non handler callers6m 50s23s18×$3.84$0.1232×11.3M107K2
Q14 · api docs2m 38s22s7.1×$0.70$0.154.6×1.0M135K4
Q15 · dead rpcs2m 47s39s4.2×$0.49$0.242.0×893K337K5
Q16 · pii flow3m 29s41s5.1×$0.79$0.213.8×1.8M296K7
Q17 · response consumers4m 26s27s9.8×$1.22$0.186.8×2.7M152K4
Q18 · handler fanout46s40s1.2×$0.20$0.290.7×272K479K4
Q19 · domain dependencies3m 29s19s11×$0.85$0.0949.0×2.0M72K1
Q20 · proto change repos1m 37s39s2.5×$0.54$0.262.1×900K194K22
All 20 prompts82m 34s12m 15s6.7×$22.05$3.686.0×64.3M4.2M

Accuracy

Each answer scores the share of the facts it must state, such as every calling service, that it names. The facts come from the fixtures' oracles (expected.json, written by the generator of the code, not by ProtoMap) and were checked by hand where ProtoMap and the oracle disagreed; the agents could not read the oracles, which were moved out of the workspace. Mean of 2 runs.

PromptOpus 5.5Sonnet 5.5Facts graded
BaselineProtoMapBaselineProtoMap
Q01 · list services100%100%100%100%110
Q02 · most connected80%100%80%100%top 10
Q03 · service dependents100%100%100%100%16
Q04 · rpc callers96%100%93%100%14
Q05 · field blast radius100%100%100%100%12
Q06 · request field change100%100%100%100%17
Q07 · response provenance100%100%100%100%4
Q08 · two hop lineage100%100%100%100%5
Q09 · remove rpc100%100%100%100%17
Q10 · outbound dependencies96%100%92%100%12
Q11 · transitive impact100%100%100%100%18
Q12 · call chain100%100%100%100%4
Q13 · non handler callers100%100%100%100%25
Q14 · api docs100%100%100%100%21
Q15 · dead rpcs100%100%100%100%4 dead of 9
Q16 · pii flow96%100%100%100%12
Q17 · response consumers100%100%100%100%8
Q18 · handler fanout100%100%100%100%6
Q19 · domain dependencies100%100%100%100%43
Q20 · proto change repos100%100%100%100%6

The prompts

Five are the chat panel's quickstart prompts with real names filled in; the other fifteen need several services, several repositories, or several protobuf messages to answer. Every prompt was given as written, after the same one-line preamble: Answer this question about the code in the current directory, which holds Acme's service repositories. Work read-only: do not modify any files. Be concise and exact: give full names, and file:line where asked.

  1. Quickstart Q01 List all the gRPC services implemented in the Swift repo (protomap-test-swift). Give the fully qualified name of each and how many RPCs it has.
  2. Quickstart Q02 Show the 10 most connected gRPC services across all our repos: the ones with the most distinct other services calling them or being called by them. Give the inbound and outbound service counts for each.
  3. Quickstart Q03 Which services depend on acme.loyalty.v1.LoyaltyMemberQueryService? For each dependent service, tell me which repo it lives in and which RPCs it calls.
  4. Quickstart Q04 Which services call acme.customers.v1.CustomerService/ListCustomers? List every call site with file:line and the enclosing function, across all repos.
  5. Quickstart Q05 What is the blast radius of changing acme.ledger.v1.LedgerAccount.currency_code? Which services actually use this field's data (read it and pass it along to another RPC or into a response), and through which RPCs?
  6. Cross-service Q06 We're replacing the `customer` field of acme.customers.v1.UpdateCustomerRequest with a new message. Which code, in which repos, sets that field when calling acme.customers.v1.CustomerService/UpdateCustomer, and to which other RPCs does the UpdateCustomer handler pass the field on?
  7. Cross-service Q07 Where does the `cart` in the response of acme.cart.v1.CartCalculationService/SimulateCart come from? Trace it back to the upstream RPC responses or request fields it is built from.
  8. Cross-service Q08 Trace the `payout` field of acme.payments.v1.SchedulePayoutExpiryRequest two hops downstream: which RPCs does the SchedulePayoutExpiry handler send it to, and where do the handlers of those RPCs send it next? Name the services and repos at each hop.
  9. Cross-service Q09 We want to remove the RPC acme.documents.v1.DocumentQueryService/ResolveDocuments. Find every place in every repo that calls it, including indirect calls through helpers, method names built from strings, registries, or reflection, and give file:line and the calling service for each so we can migrate them.
  10. Cross-service Q10 What does acme.payments.v1.PaymentCalculationService depend on? List every downstream service it calls, which RPCs, and which repo owns each downstream service.
  11. Cross-service Q11 If acme.pricing.v1.PriceListQueryService goes down, which services are affected? Include direct callers and services that depend on it transitively through the RPCs that call it, up to 3 hops away, and say how many hops away each one is.
  12. Cross-service Q12 Can a request to acme.ticketing.v1.TicketTransferWorkflowService end up calling acme.ledger.v1.LedgerAccountValidationService? Show the shortest call chain hop by hop: the RPC called at each hop, and the repo and file:line where each call is made.
  13. Cross-service Q13 Which code outside of gRPC handlers (event workers, batch jobs, CLIs, server startup) calls the ledger services (acme.ledger.v1.*)? List each caller, its repo, and the ledger RPCs it calls.
  14. Cross-service Q14 Write API reference docs for acme.giftcards.v1.GiftCardService: for each RPC, its request and response messages, the file that implements it, and which services call it.
  15. Cross-service Q15 Which RPCs of acme.checkout.v1.CheckoutSessionService are never called by any code in any of our repos? We want to delete dead endpoints, so don't list an RPC that is actually called somewhere. End your answer with one line of the form `UNUSED: <comma-separated RPC names>`.
  16. Cross-service Q16 For a privacy review: which services' code actually reads a patient's national ID (acme.patients.v1.Patient.national_id) and passes it on, and through which RPCs does it travel?
  17. Cross-service Q17 If acme.customers.v1.CustomerCalculationService/ExplainCustomerCalculation stopped returning the `result` field of its response, which callers would be affected? For each caller, which RPC requests or responses does that data flow into?
  18. Cross-service Q18 What does the handler of acme.shipping.v1.ShipmentCalculationService/CalculateShipment call downstream, and which fields of its request end up in each downstream call?
  19. Cross-service Q19 Which other domains does the checkout domain (acme.checkout.v1) call into, and which domains call checkout? Summarize the dependencies in both directions.
  20. Cross-service Q20 We're making a breaking change to the acme.subscriptions.v1 protos. Which repos call or implement subscriptions services and would need to update and redeploy? For each repo, which subscriptions services does it use?

The codebase

Acme's nine repositories, generated from one catalog of APIs and each in a different language, call each other's services and copy each other's protobuf files, as an enterprise's do. Their code moves data through patterns that are hard to follow statically: method paths built from strings, registries, reflection, futures, callbacks, event buses, and framework dispatch. The indexing time is a fresh protomap scan of each repository on one machine.

RepositoryLanguageLines (tracked) ServicesCallsField flowsIndex time
protomap-test-csharpC#400,9583407,65327,3975 s
protomap-test-golangGo2,590,7544806,92337,7479 s
protomap-test-javaJava590,0634006,02732,2154 s
protomap-test-javascriptJavaScript166,2101802,56713,7303 s
protomap-test-kotlinKotlin235,0411904,42015,5422 s
protomap-test-pythonPython298,0602403,72619,1775 s
protomap-test-rustRust242,1151501,99610,8832 s
protomap-test-swiftSwift152,4931101,9749,6902 s
protomap-test-typescriptTypeScript314,7232904,22622,8734 s
All nine7 languages + protobuf4,990,4172,38039,512189,25435 s

4.99M tracked lines: 1.64M of service code, 1.23M of protobuf (each repository holds copies of the files of the services it calls), and 2.12M of generated Go bindings, which the Go repository checks in. Calls and flows are the oracles' counts of call sites and field-level dataflows the code makes.

How it was run

  • Each run is a fresh claude -p session (Claude Code 2.1.306) in ~/protomap-test, which holds only the nine repositories, with nothing the agents could take answers from: the fixtures' answer keys, build outputs, and other projects were moved out for the runs.
  • Both arms have the same read-only tools: Read, Grep, Glob, subagents, and read-only shell commands (rg, grep, find, git grep, awk, jq, and the like); anything else is denied. The ProtoMap arm adds the ProtoMap MCP server (Streamable HTTP, the shared graph of the nine repositories); the baseline arm has no MCP servers.
  • User settings, CLAUDE.md files, and hooks were left out (--setting-sources project,local) so nothing but the prompt differs between runs.
  • Opus 5.5 ran at high effort and Sonnet 5.5 at extra-high effort. Every prompt ran twice per model and arm (160 runs; a third round was cut short by usage limits and discarded); the charts show the median, which for two runs is their mean, and accuracy is the mean.
  • Time is wall-clock time of the whole session; cost and tokens are what Claude Code reports at API prices, subagents and cache reads included. A run is stopped after 45 minutes and counts its 45 minutes and the cost it had reached.
  • Grading is by code, with no model as judge: each answer must name the services, methods, repositories, or fields of its prompt's answer key (by full or short name, or the file its implementation is in). The dead-RPC prompt scores the F1 of the list the answer ends with, so a wrong RPC costs as much as a missed one.

Total spend across all runs at API prices: $163.11.

Stop wasting tokens on discovery.

Install the self-hosted ProtoMap platform and CLI scanner for free, and give your engineers and agents org-wide context.