From 220ad4de9b2520ac29811c3e912bd67b2604bcfb Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Thu, 20 Aug 2026 20:54:40 -0700 Subject: [PATCH 1/2] feat(research): add React Native contract providers --- llp/0013-platform-research.explainer.md | 11 ++- scripts/research/evaluate-corpora.mjs | 88 +++++++++++++++++++ .../research-mcp/direct-fetch.test.ts | 12 +++ src/__tests__/research-mcp/mcp.test.ts | 13 ++- src/__tests__/research-mcp/providers.test.ts | 54 ++++++++++++ .../research-mcp/remote-search.test.ts | 64 ++++++++++++++ .../research-mcp/search-index.test.ts | 34 +++++++ src/research-mcp/direct-fetch.ts | 16 ++++ src/research-mcp/providers.ts | 72 +++++++++++++++ src/research-mcp/remote-search.ts | 32 +++++++ src/research-mcp/server.ts | 6 +- src/research-mcp/types.ts | 9 ++ 12 files changed, 407 insertions(+), 4 deletions(-) diff --git a/llp/0013-platform-research.explainer.md b/llp/0013-platform-research.explainer.md index bbb6c77..15aa01a 100644 --- a/llp/0013-platform-research.explainer.md +++ b/llp/0013-platform-research.explainer.md @@ -228,7 +228,16 @@ the queries and bounded results. The built-in source catalog covers Apple, Android, platform releases, Swift Evolution, SDWebImage, Media3, Glide, OkHttp, Kotlin coroutines, Gradle/AGP, selected JetBrains -issues, Expo, React Native, Reanimated, Gesture Handler, Screens, and Worklets. +issues, Expo, React, React Native, WHATWG and W3C web specifications, Chrome DevTools +Protocol, Flow, TypeScript, the Android NDK, CMake, CocoaPods, Metro, Reanimated, +Gesture Handler, Screens, and Worklets. The React Native +additions cover the contracts its implementation must preserve: React semantics, its +web-compatible APIs, the repository's type systems, and its native build and dependency +managers. Hermes documentation is intentionally not fetched from the engine's `main` +branch because it can disagree with React Native's vendored revision. Repository PR and +issue history is intentionally not a +live evidence provider: it belongs in a versioned offline evaluation corpus used to +improve prompts, while the runtime MCP remains an API-contract channel. The canonical OkHttp host is backed by both the [maintainer's transfer announcement](https://jakewharton.com/the-lysine-contingency/) and [Commonhaus's project-transfer record](https://www.commonhaus.org/activity/315.html). diff --git a/scripts/research/evaluate-corpora.mjs b/scripts/research/evaluate-corpora.mjs index 1192de0..5d78fa8 100644 --- a/scripts/research/evaluate-corpora.mjs +++ b/scripts/research/evaluate-corpora.mjs @@ -141,6 +141,94 @@ const cases = [ title: "FlatList", body: "getItemLayout is an optional optimization for FlatList. It lets the list skip the measurement of dynamic content when the row height is known ahead of time.", }, + { + label: "react", + provider: "react", + platform: "react-native", + providers: ["react"], + query: "useEffect dependency array", + expected: "react.dev/reference/react/useEffect", + url: "https://react.dev/reference/react/useEffect", + title: "useEffect", + body: "useEffect synchronizes a component with an external system. Every reactive value read by the effect belongs in its dependency array.", + }, + { + label: "flow", + provider: "flow", + platform: "react-native", + providers: ["flow"], + query: "exact object types", + expected: "flow.org/en/docs/types/objects", + url: "https://flow.org/en/docs/types/objects/", + title: "Object Types", + body: "An exact object type rejects extra properties. Flow object types are exact by default unless they are explicitly declared inexact.", + }, + { + label: "typescript", + provider: "typescript", + platform: "react-native", + providers: ["typescript"], + query: "declaration file module exports", + expected: "typescriptlang.org/docs/handbook/declaration-files", + url: "https://www.typescriptlang.org/docs/handbook/declaration-files/templates/module-d-ts.html", + title: "Modules .d.ts", + body: "A module declaration file describes the runtime exports of a JavaScript module. Its export shape must match the values consumers can import.", + }, + { + label: "web-standards", + provider: "web-standards", + platform: "react-native", + providers: ["web-standards"], + sourceKind: "standard", + query: "AbortSignal fetch cancellation", + expected: "fetch.spec.whatwg.org", + url: "https://fetch.spec.whatwg.org/", + title: "Fetch Standard", + body: "An AbortSignal can abort a fetch operation. The fetch promise rejects when the request is terminated by the signal.", + }, + { + label: "chrome-devtools-protocol", + provider: "chrome-devtools-protocol", + platform: "react-native", + providers: ["chrome-devtools-protocol"], + sourceKind: "standard", + query: "Runtime evaluate awaitPromise", + expected: "chromedevtools.github.io/devtools-protocol/tot/Runtime", + url: "https://chromedevtools.github.io/devtools-protocol/tot/Runtime/", + title: "Runtime domain", + body: "Runtime.evaluate evaluates an expression on the inspected page. The awaitPromise option waits for a returned promise and reports its resolved result.", + }, + { + label: "android-ndk", + provider: "android-ndk", + providers: ["android-ndk"], + query: "JNIEnv local references", + expected: "developer.android.com/ndk/guides/jni-tips", + url: "https://developer.android.com/ndk/guides/jni-tips", + title: "JNI tips", + body: "JNI local references are valid only for the duration of the current native method call and on the current thread. Long-lived references must be promoted to global references.", + }, + { + label: "cmake", + provider: "cmake", + providers: ["cmake"], + query: "target_link_libraries transitive", + expected: "cmake.org/cmake/help/latest/command/target_link_libraries", + url: "https://cmake.org/cmake/help/latest/command/target_link_libraries.html", + title: "target_link_libraries", + body: "target_link_libraries links a target to libraries and can publish transitive link dependencies through the target's interface.", + }, + { + label: "cocoapods", + provider: "cocoapods", + platform: "apple", + providers: ["cocoapods"], + query: "Podfile use_frameworks linkage", + expected: "guides.cocoapods.org/syntax/podfile", + url: "https://guides.cocoapods.org/syntax/podfile.html", + title: "Podfile Syntax Reference", + body: "The use_frameworks directive configures pods to build as frameworks and accepts a linkage option for static or dynamic frameworks.", + }, { label: "react-native-reanimated", provider: "react-native-reanimated", diff --git a/src/__tests__/research-mcp/direct-fetch.test.ts b/src/__tests__/research-mcp/direct-fetch.test.ts index 9032d0a..b04a8e9 100644 --- a/src/__tests__/research-mcp/direct-fetch.test.ts +++ b/src/__tests__/research-mcp/direct-fetch.test.ts @@ -221,4 +221,16 @@ test("direct URL inference prefers narrow release and dependency providers", () "https://developer.android.com/reference/androidx/media3/common/Player", ).provider, ).toBe("media3"); + expect( + resolveDirectDocumentationTarget("https://developer.android.com/ndk/guides/cpp-support"), + ).toMatchObject({ provider: "android-ndk", sourceKind: "official-guide" }); + expect(resolveDirectDocumentationTarget("https://fetch.spec.whatwg.org/")).toMatchObject({ + provider: "web-standards", + sourceKind: "standard", + }); + expect( + resolveDirectDocumentationTarget( + "https://chromedevtools.github.io/devtools-protocol/tot/Runtime/", + ), + ).toMatchObject({ provider: "chrome-devtools-protocol", sourceKind: "standard" }); }); diff --git a/src/__tests__/research-mcp/mcp.test.ts b/src/__tests__/research-mcp/mcp.test.ts index 3d8a269..3339373 100644 --- a/src/__tests__/research-mcp/mcp.test.ts +++ b/src/__tests__/research-mcp/mcp.test.ts @@ -40,12 +40,17 @@ test("stdio MCP advertises the read-only documentation tools and their enforced expect(searchTool?.description ?? "").toContain("Native source retains platform context"); expect(searchTool?.description ?? "").toContain("react-native-worklets=Worklets"); expect(searchTool?.description ?? "").toContain("metro=Metro bundler"); + expect(searchTool?.description ?? "").toContain( + "web-standards=WHATWG and W3C web platform standards", + ); + expect(searchTool?.description ?? "").toContain("android-ndk=Android NDK"); expect(fetchTool?.annotations?.readOnlyHint).toBe(true); expect(fetchTool?.description ?? "").toMatch(/every redirect/); const inputSchema = searchTool?.inputSchema as { properties?: { query?: { description?: string }; - providers?: { description?: string }; + providers?: { description?: string; items?: { enum?: string[] } }; + sourceKinds?: { description?: string; items?: { enum?: string[] } }; }; }; const fetchInputSchema = fetchTool?.inputSchema as { @@ -60,6 +65,12 @@ test("stdio MCP advertises the read-only documentation tools and their enforced expect(inputSchema.properties?.providers?.description ?? "").toContain( "jetbrains-issues=JetBrains YouTrack context", ); + expect(inputSchema.properties?.providers?.items?.enum).toContain("typescript"); + expect(inputSchema.properties?.providers?.items?.enum).toContain("chrome-devtools-protocol"); + expect(inputSchema.properties?.sourceKinds?.items?.enum).toContain("standard"); + expect(inputSchema.properties?.sourceKinds?.description ?? "").toContain( + "external compatibility specification", + ); expect(fetchInputSchema.properties?.context?.default).toBe("section"); expect(fetchInputSchema.properties?.context?.description ?? "").toContain( "document=bounded extracted page text", diff --git a/src/__tests__/research-mcp/providers.test.ts b/src/__tests__/research-mcp/providers.test.ts index 1486ea8..0564676 100644 --- a/src/__tests__/research-mcp/providers.test.ts +++ b/src/__tests__/research-mcp/providers.test.ts @@ -1,10 +1,15 @@ import { expect, test } from "bun:test"; import { + androidNdkProvider, androidProvider, appleProvider, appleReleasesProvider, + chromeDevtoolsProtocolProvider, + cmakeProvider, + cocoaPodsProvider, expoProvider, + flowProvider, glideProvider, jetbrainsIssuesProvider, metroProvider, @@ -14,10 +19,13 @@ import { reactNativeReanimatedProvider, reactNativeScreensProvider, reactNativeWorkletsProvider, + reactProvider, resolveAllowedUrl, resolveAllowedRequestUrl, sdWebImageProvider, swiftEvolutionProvider, + typescriptProvider, + webStandardsProvider, } from "../../research-mcp/providers.js"; test("Apple provider accepts only HTTPS documentation paths on the exact host", () => { @@ -199,3 +207,49 @@ test("React Native ecosystem providers stay on their exact documentation hosts a resolveAllowedUrl(metroProvider, "https://metrobundler.dev.evil.example/docs/configuration"), ).toThrow(/outside the metro documentation allowlist/); }); + +test("React Native contract providers admit only their fixed authoritative corpora", () => { + for (const [provider, url] of [ + [reactProvider, "https://react.dev/reference/react/useEffect"], + [flowProvider, "https://flow.org/en/docs/types/unions/"], + [typescriptProvider, "https://www.typescriptlang.org/docs/handbook/declaration-files/"], + [androidNdkProvider, "https://developer.android.com/ndk/guides/cpp-support"], + [cmakeProvider, "https://cmake.org/cmake/help/v3.31/command/add_library.html"], + [cocoaPodsProvider, "https://guides.cocoapods.org/syntax/podfile.html"], + [webStandardsProvider, "https://fetch.spec.whatwg.org/"], + [webStandardsProvider, "https://w3c.github.io/IntersectionObserver/"], + [ + chromeDevtoolsProtocolProvider, + "https://chromedevtools.github.io/devtools-protocol/tot/Runtime/", + ], + ] as const) { + expect(resolveAllowedUrl(provider, url).hostname).toBe(new URL(url).hostname); + } + + for (const [provider, url] of [ + [reactProvider, "https://react.dev/reference/react-dom/components/form"], + [reactProvider, "https://react.dev/learn/escape-hatches"], + [flowProvider, "https://flow.org/blog/"], + [typescriptProvider, "https://www.typescriptlang.org/play"], + [androidNdkProvider, "https://developer.android.com/studio/projects/install-ndk"], + [cmakeProvider, "https://cmake.org/community/"], + [cocoaPodsProvider, "https://guides.cocoapods.org/blog/"], + [webStandardsProvider, "https://w3c.github.io/permissions/"], + [webStandardsProvider, "https://whatwg.org/"], + [chromeDevtoolsProtocolProvider, "https://chromedevtools.github.io/debugger-protocol-viewer/"], + ] as const) { + expect(() => resolveAllowedUrl(provider, url)).toThrow( + /outside the .* documentation allowlist/, + ); + } + + expect(() => + resolveAllowedUrl(webStandardsProvider, "https://user:password@fetch.spec.whatwg.org/"), + ).toThrow(/outside the web-standards documentation allowlist/); + expect(() => + resolveAllowedUrl(androidNdkProvider, "https://developer.android.com.evil.example/ndk/guides"), + ).toThrow(/outside the android-ndk documentation allowlist/); + expect(() => + resolveAllowedUrl(reactNativeProvider, "https://reactnative.dev/contributing/overview"), + ).toThrow(/outside the react-native documentation allowlist/); +}); diff --git a/src/__tests__/research-mcp/remote-search.test.ts b/src/__tests__/research-mcp/remote-search.test.ts index d808b10..dc62c9f 100644 --- a/src/__tests__/research-mcp/remote-search.test.ts +++ b/src/__tests__/research-mcp/remote-search.test.ts @@ -299,3 +299,67 @@ test("Apple release searches select the platform-specific fixed scope", async () expect(queries[0]).toContain("site:developer.apple.com/documentation/xcode-release-notes"); expect(queries[1]).toContain("site:developer.apple.com/documentation/ios-ipados-release-notes"); }); + +test("web standards discovery uses fixed WHATWG and W3C scopes with standards provenance", async () => { + let searchRequests = 0; + const fetchImplementation = async (input: string | URL | Request): Promise => { + const url = new URL(input instanceof Request ? input.url : input.toString()); + if (url.hostname === "api.search.brave.com") { + searchRequests++; + const query = url.searchParams.get("q") ?? ""; + expect(query).toContain("site:whatwg.org"); + expect(query).toContain("site:w3c.github.io"); + return jsonResponse({ + web: { + results: [ + { title: "Fetch Standard", url: "https://fetch.spec.whatwg.org/" }, + { title: "Unrelated W3C draft", url: "https://w3c.github.io/permissions/" }, + ], + }, + }); + } + expect(url.href).toBe("https://fetch.spec.whatwg.org/"); + return new Response( + "Fetch Standard
" + + "

Fetch

The AbortSignal controls cancellation of a fetch request and " + + "causes the fetch promise to reject when the operation is terminated.

" + + "
", + { headers: { "content-type": "text/html; charset=utf-8" } }, + ); + }; + + const response = await searchRemoteDocumentation( + "web-standards", + "AbortSignal fetch cancellation", + 2, + { apiKey: "test-key", fetchImplementation, sourceKinds: ["standard"] }, + ); + + expect(searchRequests).toBe(1); + expect(response.warnings).toEqual([]); + expect(response.results).toHaveLength(1); + expect(response.results[0]).toMatchObject({ + provider: "web-standards", + sourceKind: "standard", + url: "https://fetch.spec.whatwg.org/", + }); +}); + +test("provenance filtering skips a standards provider before network access", async () => { + let requests = 0; + const response = await searchRemoteDocumentation( + "chrome-devtools-protocol", + "Runtime evaluate", + 1, + { + apiKey: "test-key", + sourceKinds: ["official-api"], + fetchImplementation: async () => { + requests++; + return jsonResponse({ web: { results: [] } }); + }, + }, + ); + expect(requests).toBe(0); + expect(response).toEqual({ results: [], warnings: [] }); +}); diff --git a/src/__tests__/research-mcp/search-index.test.ts b/src/__tests__/research-mcp/search-index.test.ts index 925bc11..5746cf1 100644 --- a/src/__tests__/research-mcp/search-index.test.ts +++ b/src/__tests__/research-mcp/search-index.test.ts @@ -149,6 +149,40 @@ test("search filters named corpora and provenance classes", () => { ]); }); +test("search filters external standards separately from implementation documentation", () => { + const index = buildSearchIndex([ + { + id: "standard:fetch", + platform: "react-native", + provider: "web-standards", + sourceKind: "standard", + title: "Fetch Standard", + url: "https://fetch.spec.whatwg.org/", + passage: "AbortSignal controls cancellation of a fetch request.", + indexedAt: "2026-08-06T00:00:00.000Z", + }, + { + id: "rn:fetch", + platform: "react-native", + provider: "react-native", + sourceKind: "official-api", + title: "React Native global fetch", + url: "https://reactnative.dev/docs/global-fetch", + passage: "React Native exposes fetch as a global API.", + indexedAt: "2026-08-06T00:00:00.000Z", + }, + ]); + + const results = searchDocumentation(index, "fetch AbortSignal", { + platform: "react-native", + sourceKinds: ["standard"], + limit: 5, + }); + expect(results.map(({ provider, sourceKind }) => ({ provider, sourceKind }))).toEqual([ + { provider: "web-standards", sourceKind: "standard" }, + ]); +}); + test("a language filter excludes otherwise matching untagged documents", () => { const index = buildSearchIndex([ ...chunks, diff --git a/src/research-mcp/direct-fetch.ts b/src/research-mcp/direct-fetch.ts index fd76b03..d1d4de8 100644 --- a/src/research-mcp/direct-fetch.ts +++ b/src/research-mcp/direct-fetch.ts @@ -22,12 +22,20 @@ const DIRECT_PROVIDER_ORDER: readonly ProviderId[] = [ "android-releases", "media3", "agp", + "android-ndk", "android", "glide", "okhttp", "kotlin-coroutines", "gradle", "jetbrains-issues", + "react", + "web-standards", + "chrome-devtools-protocol", + "flow", + "typescript", + "cmake", + "cocoapods", "react-native-reanimated", "react-native-gesture-handler", "react-native-screens", @@ -50,9 +58,17 @@ const DIRECT_SOURCE_KIND: Record = { "kotlin-coroutines": "official-guide", gradle: "official-guide", agp: "release-notes", + "android-ndk": "official-guide", "jetbrains-issues": "issue-tracker", expo: "official-api", + react: "official-api", "react-native": "official-api", + "web-standards": "standard", + "chrome-devtools-protocol": "standard", + flow: "official-guide", + typescript: "official-guide", + cmake: "official-guide", + cocoapods: "official-guide", "react-native-reanimated": "official-guide", "react-native-gesture-handler": "official-guide", "react-native-screens": "official-guide", diff --git a/src/research-mcp/providers.ts b/src/research-mcp/providers.ts index 36ed3a8..d417827 100644 --- a/src/research-mcp/providers.ts +++ b/src/research-mcp/providers.ts @@ -345,6 +345,10 @@ export const expoProvider = htmlProvider("expo", "react-native", "Expo documenta { hostname: "docs.expo.dev", prefixes: [""] }, ]); +export const reactProvider = htmlProvider("react", "react-native", "React documentation", [ + { hostname: "react.dev", prefixes: ["/reference/react", "/reference/rules"] }, +]); + export const reactNativeProvider = htmlProvider( "react-native", "react-native", @@ -357,6 +361,66 @@ export const reactNativeProvider = htmlProvider( ], ); +export const webStandardsProvider = htmlProvider( + "web-standards", + "react-native", + "Web platform standards", + [ + { hostname: "fetch.spec.whatwg.org", prefixes: [""] }, + { hostname: "xhr.spec.whatwg.org", prefixes: [""] }, + { hostname: "websockets.spec.whatwg.org", prefixes: [""] }, + { hostname: "dom.spec.whatwg.org", prefixes: [""] }, + { hostname: "streams.spec.whatwg.org", prefixes: [""] }, + { hostname: "url.spec.whatwg.org", prefixes: [""] }, + { hostname: "encoding.spec.whatwg.org", prefixes: [""] }, + { hostname: "console.spec.whatwg.org", prefixes: [""] }, + { hostname: "html.spec.whatwg.org", prefixes: ["/multipage"] }, + { + hostname: "w3c.github.io", + prefixes: [ + "/FileAPI", + "/hr-time", + "/performance-timeline", + "/resource-timing", + "/IntersectionObserver", + ], + }, + ], +); + +export const chromeDevtoolsProtocolProvider = htmlProvider( + "chrome-devtools-protocol", + "react-native", + "Chrome DevTools Protocol documentation", + [{ hostname: "chromedevtools.github.io", prefixes: ["/devtools-protocol"] }], +); + +export const flowProvider = htmlProvider("flow", "react-native", "Flow documentation", [ + { hostname: "flow.org", prefixes: ["/en/docs"] }, +]); + +export const typescriptProvider = htmlProvider( + "typescript", + "react-native", + "TypeScript documentation", + [{ hostname: "www.typescriptlang.org", prefixes: ["/docs"] }], +); + +export const androidNdkProvider = htmlProvider( + "android-ndk", + "android", + "Android NDK documentation", + [{ hostname: "developer.android.com", prefixes: ["/ndk"] }], +); + +export const cmakeProvider = htmlProvider("cmake", "android", "CMake documentation", [ + { hostname: "cmake.org", prefixes: ["/cmake/help"] }, +]); + +export const cocoaPodsProvider = htmlProvider("cocoapods", "apple", "CocoaPods guides", [ + { hostname: "guides.cocoapods.org", prefixes: ["/syntax", "/using"] }, +]); + export const reactNativeReanimatedProvider = htmlProvider( "react-native-reanimated", "react-native", @@ -450,7 +514,15 @@ const providers: Record = { agp: agpProvider, "jetbrains-issues": jetbrainsIssuesProvider, expo: expoProvider, + react: reactProvider, "react-native": reactNativeProvider, + "web-standards": webStandardsProvider, + "chrome-devtools-protocol": chromeDevtoolsProtocolProvider, + flow: flowProvider, + typescript: typescriptProvider, + "android-ndk": androidNdkProvider, + cmake: cmakeProvider, + cocoapods: cocoaPodsProvider, "react-native-reanimated": reactNativeReanimatedProvider, "react-native-gesture-handler": reactNativeGestureHandlerProvider, "react-native-screens": reactNativeScreensProvider, diff --git a/src/research-mcp/remote-search.ts b/src/research-mcp/remote-search.ts index 61e9559..f0f3781 100644 --- a/src/research-mcp/remote-search.ts +++ b/src/research-mcp/remote-search.ts @@ -71,10 +71,42 @@ const providerSearchDefinitions: Record, ProviderSea scopes: ["youtrack.jetbrains.com/issue"], sourceKind: "issue-tracker", }, + react: { + scopes: ["react.dev/reference/react", "react.dev/reference/rules"], + sourceKind: "official-api", + }, "react-native": { scopes: ["reactnative.dev"], sourceKind: "official-api", }, + "web-standards": { + scopes: ["whatwg.org", "w3c.github.io"], + sourceKind: "standard", + }, + "chrome-devtools-protocol": { + scopes: ["chromedevtools.github.io/devtools-protocol"], + sourceKind: "standard", + }, + flow: { + scopes: ["flow.org/en/docs"], + sourceKind: "official-guide", + }, + typescript: { + scopes: ["typescriptlang.org/docs"], + sourceKind: "official-guide", + }, + "android-ndk": { + scopes: ["developer.android.com/ndk"], + sourceKind: "official-guide", + }, + cmake: { + scopes: ["cmake.org/cmake/help/latest"], + sourceKind: "official-guide", + }, + cocoapods: { + scopes: ["guides.cocoapods.org/syntax", "guides.cocoapods.org/using"], + sourceKind: "official-guide", + }, "react-native-reanimated": { scopes: ["docs.swmansion.com/react-native-reanimated/docs"], sourceKind: "official-api", diff --git a/src/research-mcp/server.ts b/src/research-mcp/server.ts index fb5270b..b689f8f 100644 --- a/src/research-mcp/server.ts +++ b/src/research-mcp/server.ts @@ -25,7 +25,7 @@ const queryGuidance = "Formulate short documentation queries from exact API symbols plus one behavior or constraint term. Good: `CameraView barcodeScannerSettings`, `NWPathMonitor pathUpdateHandler`, `GestureDetector simultaneous gestures`. Avoid questions, prose, package/import names, code snippets, literals, paths, credentials, and other sensitive context. If the first result is broad, retry with a narrower symbol or member name."; const providerGuidance = - "Provider map: apple=Apple SDK APIs and Human Interface Guidelines; apple-releases=Xcode and Apple platform release notes; swift-evolution=Swift Evolution proposals; sdwebimage=SDWebImage APIs and caching/loading behavior; android=Android, Jetpack, Compose, and Google Play services APIs; android-releases=Android platform releases and behavior changes; media3=Jetpack Media3; glide=Glide; okhttp=OkHttp; kotlin-coroutines=Kotlin coroutines; gradle=Gradle; agp=Android Gradle Plugin; jetbrains-issues=JetBrains YouTrack context; expo=Expo documentation; react-native=React Native core; react-native-reanimated=Reanimated; react-native-gesture-handler=Gesture Handler; react-native-screens=Screens; react-native-worklets=Worklets; metro=Metro bundler. Native source retains platform context: use apple/android for OS contracts and add the dependency provider for dependency-owned behavior; an Expo package path does not make a native API an Expo-docs query. Issue-tracker results are context, not API contracts."; + "Provider map: apple=Apple SDK APIs and Human Interface Guidelines; apple-releases=Xcode and Apple platform release notes; swift-evolution=Swift Evolution proposals; sdwebimage=SDWebImage APIs and caching/loading behavior; android=Android, Jetpack, Compose, and Google Play services APIs; android-releases=Android platform releases and behavior changes; android-ndk=Android NDK; media3=Jetpack Media3; glide=Glide; okhttp=OkHttp; kotlin-coroutines=Kotlin coroutines; gradle=Gradle; agp=Android Gradle Plugin; jetbrains-issues=JetBrains YouTrack context; expo=Expo documentation; react=React APIs and semantics; react-native=React Native core documentation; web-standards=WHATWG and W3C web platform standards; chrome-devtools-protocol=Chrome DevTools Protocol; flow=Flow type-system documentation; typescript=TypeScript declaration semantics; cmake=CMake build-system documentation; cocoapods=CocoaPods integration guides; react-native-reanimated=Reanimated; react-native-gesture-handler=Gesture Handler; react-native-screens=Screens; react-native-worklets=Worklets; metro=Metro bundler. Native source retains platform context: use apple/android for OS contracts and add the dependency, build-tool, type-system, or web-contract provider for behavior that source owns; repository ownership does not make a native API a React Native or Expo documentation query. Standards describe compatibility targets; confirm the reviewed implementation claims that target before applying them. Issue-tracker results are context, not API contracts."; export interface DocumentationServerOptions { braveApiKey?: string; @@ -84,7 +84,9 @@ export async function createDocumentationServer(options: DocumentationServerOpti .min(1) .max(SOURCE_KINDS.length) .optional() - .describe("Optional provenance classes to search"), + .describe( + "Optional provenance classes to search; standard denotes an external compatibility specification rather than an implementation-specific API page", + ), }, annotations: { readOnlyHint: true, diff --git a/src/research-mcp/types.ts b/src/research-mcp/types.ts index 36a1903..c2a222e 100644 --- a/src/research-mcp/types.ts +++ b/src/research-mcp/types.ts @@ -18,7 +18,15 @@ export const PROVIDERS = [ "agp", "jetbrains-issues", "expo", + "react", "react-native", + "web-standards", + "chrome-devtools-protocol", + "flow", + "typescript", + "android-ndk", + "cmake", + "cocoapods", "react-native-reanimated", "react-native-gesture-handler", "react-native-screens", @@ -31,6 +39,7 @@ export type ProviderId = (typeof PROVIDERS)[number]; export const SOURCE_KINDS = [ "official-api", "official-guide", + "standard", "release-notes", "issue-tracker", ] as const; From a1336029645d79730d6c48d0de6368a6fc459958 Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Thu, 20 Aug 2026 20:54:54 -0700 Subject: [PATCH 2/2] docs: correct research cache behavior --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cb47bd5..b2c7b79 100644 --- a/README.md +++ b/README.md @@ -274,8 +274,8 @@ The boundary, in brief: many discovery and page fetches, so each call reports its own request ledger and the run reports totals. - **Root-only in routed monorepos** (it starts a host process); scope configs - cannot alter it. Result-cache reuse is disabled while research is enabled, - because web content can change without a config change. + cannot alter it. Research results are fetched and audited per run; cached review + results remain keyed by the ordinary trusted review inputs and make no network call. Full detail — providers, query grammar, `fetch_platform_doc` modes, provenance and citation grounding: [LLP 0013](./llp/0013-platform-research.explainer.md).