Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

OpenCtx is an open standard for annotating code with contextual information from various development tools. It's a TypeScript monorepo using pnpm workspaces.

## Build Commands

```bash
# Install dependencies (use correct pnpm version)
npm install -g pnpm@8.6.7
pnpm i

# Build everything
pnpm build # Runs prebuild then TypeScript compilation
pnpm bundle # Builds and bundles all packages

# Development
pnpm watch # TypeScript watch mode
pnpm generate # Run code generation

# Code quality
pnpm check # Run linting (Biome + Stylelint)
pnpm biome # Format and lint with auto-fix

# Testing
pnpm test # Run all tests
pnpm test:unit # Run unit tests once
pnpm test:integration # Run VS Code integration tests
pnpm test <pattern> # Run specific test files
```

## Architecture

### Monorepo Structure
- **lib/** - Core libraries
- `client` - Client library for consuming OpenCtx
- `protocol` - Protocol type definitions
- `provider` - Base provider implementation
- `schema` - JSON Schema definitions
- **client/** - Platform-specific client implementations (VS Code, browser, CodeMirror, Monaco)
- **provider/** - OpenCtx providers (GitHub, Storybook, Prometheus, etc.)
- **web/** - Documentation website (openctx.org)

### Provider Pattern
Providers implement the standard interface:
```typescript
interface Provider {
meta(params: MetaParams, settings: S): MetaResult
mentions?(params: MentionsParams, settings: S): Promise<MentionsResult>
items?(params: ItemsParams, settings: S): Promise<ItemsResult>
annotations?(params: AnnotationsParams, settings: S): Promise<AnnotationsResult>
}
```

### Development Workflow for Providers
1. Create provider in `provider/<name>/`
2. Must include: `index.ts`, `package.json`, `tsconfig.json`
3. Export default provider implementation
4. Bundle format: ESM for distribution
5. Settings validation should happen in provider methods

### TypeScript Configuration
- Uses project references for efficient builds
- Strict mode enabled with all checks
- Target: ESNext, Module: NodeNext
- Each package extends `tsconfig.base.json`

### Code Style
- Formatter: Biome with 4-space indentation
- Line width: 105 characters
- Single quotes, semicolons as needed
- Trailing commas enabled

### Testing
- Framework: Vitest
- Test files: `*.test.ts` or in `test/` directories
- Each package can have its own `vitest.config.ts`
- Run specific test: `pnpm test path/to/test.ts`

### Common Development Tasks

When working on providers:
1. Navigation mode: Use `isNavigation` flag in mention data
2. Page numbering: Parse with `PATTERNS.PAGE_NUMBERS` regex
3. Error handling: Use `createErrorItem()` for user-friendly errors
4. Caching: Use `QuickLRU` for API responses
5. Debouncing: Implement for search/mention operations

When working on client integrations:
1. Use `@openctx/client` for standardized client behavior
2. Implement platform-specific UI in client packages
3. Follow existing patterns in similar client implementations
3 changes: 3 additions & 0 deletions provider/context7/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
!dist
dist/*
!dist/bundle.js
78 changes: 78 additions & 0 deletions provider/context7/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Context7 context provider for OpenCtx

[OpenCtx](https://openctx.org) context provider for bringing Context7 repository documentation into code AI and editors.

## Usage

Configure your OpenCtx client:

```json
"openctx.providers": {
// ...other providers...
"https://raw.githubusercontent.com/sato-ke/openctx/refs/heads/feat/context7/provider/context7/dist/bundle.js": {
"tokens": 6000, // Maximum number of tokens to return (required)
"mentionLimit": 3 // (Optional) Maximum number of libraries per request (default: 5, max: 20)
}
}
```

### How to use in your editor

This provider supports navigation mode for AI-assisted library discovery:

1. **Navigation Mode** (get library listing):
- `@context7 <library query>` - Shows navigation with all matching libraries
- Example: `@context7 react`

2. **Direct Library Access** (after seeing navigation):
- `@context7 <library query> <page numbers>` - Access specific libraries by number
- Examples:
- `@context7 react 1` - Get library #1
- `@context7 react 1/3/5` - Get libraries #1, #3, and #5
- `@context7 react 2/4 authentication` - Get libraries #2 and #4 with authentication topic filter

## Features

- **Navigation Mode**: Browse available libraries with detailed information
- **Page Number Selection**: AI can directly access specific libraries using numbers from navigation
- **Topic Filtering**: Optional topic keyword to filter documentation content

## Context included

Repository documentation:
- Library navigation with ID, description, token count, and trust score
- Full documentation content for selected libraries
- Topic-filtered content when topic keyword is specified
- Content formatted as plain text for AI consumption

## Configuration

- `tokens` — Maximum number of tokens to return (required). Example: `6000`.
- `mentionLimit` — (Optional) Maximum number of libraries that can be selected at once (default: 3, max: 20).

## How it works

1. **Navigation Request**: When you type `@context7 <query>`, the provider searches for matching libraries and returns a navigation menu
2. **Library Selection**: AI can then request specific libraries using the numbers shown in navigation
3. **Content Fetching**: Selected libraries' documentation is fetched with optional topic filtering
4. **Token Limiting**: Content is limited to the configured token count to fit within AI context limits

## Recommended VS Code Settings

To optimize AI interaction with context7 provider, add this to your `settings.json`:

```json
{
"cody.chat.preInstruction": "### context7 provider usage\n`@context7 <library query>` - Get library navigation to see available libraries and their numbers\n`@context7 <library query> [page numbers]` - Get specific libraries using numbers from navigation (e.g., 1/3/5)\n`@context7 <library query> [page numbers] [topic]` - Get specific libraries with topic filter (e.g., 1/3 authentication)\n\n#### Important notes:\n- Page numbers are only available after seeing navigation first\n- For deep research: (1) get navigation, (2) identify relevant libraries, (3) fetch specific libraries by number\n- Topic filter helps narrow down documentation to specific areas"
}
```

**Key points for AI behavior**:
- Always start with navigation (`@context7 <query>`) to understand available libraries
- Page numbers are provider-generated and only visible in navigation responses
- Use specific page numbers for targeted documentation retrieval
- Apply topic filters when focusing on specific aspects of the library

## Development

- License: Apache 2.0
138 changes: 138 additions & 0 deletions provider/context7/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import QuickLRU from 'quick-lru'
import type { JsonDocs, SearchResponse } from './types.js'

const CONTEXT7_API_BASE_URL = 'https://context7.com/api'

const searchCache = new QuickLRU<string, SearchResponse | null>({
maxSize: 500,
maxAge: 1000 * 60 * 30,
})

function debounce<F extends (...args: any[]) => any>(
fn: F,
timeout: number,
cancelledReturn: Awaited<ReturnType<F>>,
): (...args: Parameters<F>) => Promise<Awaited<ReturnType<F>>> {
let controller = new AbortController()
let timeoutId: NodeJS.Timeout

return (...args) => {
return new Promise(resolve => {
controller.abort()

controller = new AbortController()
const { signal } = controller

timeoutId = setTimeout(async () => {
const result = await fn(...args)
resolve(result)
}, timeout)

signal.addEventListener('abort', () => {
clearTimeout(timeoutId)
resolve(cancelledReturn)
})
})
}
}

/**
* Searches for libraries matching the given query
* @param query The search query
* @returns Search results or null if the request fails
*/
export const searchLibraries = debounce(_searchLibraries, 300, { results: [] })
export async function _searchLibraries(query: string): Promise<SearchResponse | null> {
const cacheKey = `search-${query}`
if (searchCache.has(cacheKey)) {
return searchCache.get(cacheKey)!
}

try {
const url = new URL(`${CONTEXT7_API_BASE_URL}/v1/search`)
url.searchParams.set('query', query)
const response = await fetch(url)

if (!response.ok) {
console.error(`Failed to search libraries: ${response.status}`)
return null
}

const data = (await response.json()) as SearchResponse

if (data.results.length > 0) {
searchCache.set(cacheKey, data)
}
return data
} catch (error) {
console.error('Error searching libraries:', error)
return null
}
}

/**
* Fetches documentation context for a specific library
* @param libraryId The library ID to fetch documentation for
* @param tokens Number of tokens to request
* @param options Options for the request
* @returns The documentation text or null if the request fails
*/
export async function fetchLibraryDocumentation(
libraryId: string,
tokens: number,
options: {
topic?: string
} = {},
): Promise<string | null> {
try {
if (libraryId.startsWith('/')) {
libraryId = libraryId.slice(1)
}
const url = new URL(`${CONTEXT7_API_BASE_URL}/v1/${libraryId}`)
url.searchParams.set('tokens', tokens.toString())
url.searchParams.set('type', 'txt')
if (options.topic) url.searchParams.set('topic', options.topic)
const response = await fetch(url, {
headers: {
'X-Context7-Source': 'mcp-server',
},
})
if (!response.ok) {
console.error(`Failed to fetch documentation: ${response.status}`)
return null
}
const text = await response.text()
if (!text || text === 'No content available' || text === 'No context data available') {
return null
}

return text
} catch (error) {
console.error('Error fetching library documentation:', error)
return null
}
}

/**
* Process JSON response and convert it to a specific format
*
* @param {string} jsonText - The JSON text to process
* @returns {string} Converted JSON text, or the original text if processing fails
*/
export function processJsonResponse(jsonText: string): string {
try {
const data = JSON.parse(jsonText) as JsonDocs[]
const formattedData = data.map(item => ({
id: item.codeId,
title: item.codeTitle,
description: item.codeDescription,
lang: item.codeLanguage,
page: item.pageTitle,
codes: item.codeList.map(item => item.code),
}))
return JSON.stringify(formattedData)
} catch (error) {
console.error('Error processing JSON response:', error)
return jsonText
}
}
Loading
Loading