@@ -8,6 +8,8 @@ import { platform, tmpdir } from "os";
88import { spawn } from "child_process" ;
99import { join } from "path" ;
1010import { z } from "zod" ;
11+ import { parseCommandLine } from "./command-line" ;
12+ import { callContext7Api , normaliseContext7LibraryId } from "./context7" ;
1113
1214const tmp = join ( tmpdir ( ) , "pkgx-mcp" ) ;
1315
@@ -110,60 +112,6 @@ async function runPkgxCommand(program: string, args: string[], cwd?: string): Pr
110112 }
111113}
112114
113- // Parse command line with proper quote handling
114- function parseCommandLine ( cmd : string ) : [ string , string [ ] ] {
115- const args : string [ ] = [ ] ;
116- let current = '' ;
117- let inQuote = false ;
118- let quoteChar = '' ;
119-
120- for ( let i = 0 ; i < cmd . length ; i ++ ) {
121- const char = cmd [ i ] ;
122-
123- if ( char === '\\' && i + 1 < cmd . length ) {
124- // Handle escaped characters
125- current += cmd [ ++ i ] ;
126- continue ;
127- }
128-
129- if ( ( char === '"' || char === "'" ) && ! inQuote ) {
130- // Start of quoted section
131- inQuote = true ;
132- quoteChar = char ;
133- continue ;
134- }
135-
136- if ( char === quoteChar && inQuote ) {
137- // End of quoted section
138- inQuote = false ;
139- quoteChar = '' ;
140- continue ;
141- }
142-
143- if ( char === ' ' && ! inQuote ) {
144- // Space outside quotes - split argument
145- if ( current ) {
146- args . push ( current ) ;
147- current = '' ;
148- }
149- continue ;
150- }
151-
152- current += char ;
153- }
154-
155- // Add the last argument if there is one
156- if ( current ) {
157- args . push ( current ) ;
158- }
159-
160- if ( args . length === 0 ) {
161- return [ '' , [ ] ] ;
162- }
163-
164- return [ args [ 0 ] , args . slice ( 1 ) ] ;
165- }
166-
167115server . tool (
168116 "run-command-line" ,
169117 `
@@ -297,3 +245,102 @@ async function get_pkgx() {
297245 return filePath ;
298246 }
299247}
248+
249+ server . tool (
250+ "resolve-library-id" ,
251+ `
252+ Resolve a library name to Context7-compatible library IDs.
253+ This can be used before querying documentation.
254+ ` ,
255+ {
256+ libraryName : z . string ( ) . describe ( "The library name to search for, for example: react" ) ,
257+ query : z . string ( ) . describe ( "The question or task to rank matches by relevance" ) ,
258+ } ,
259+ async ( { libraryName, query } ) => {
260+ try {
261+ const responseText = await callContext7Api ( "libs/search" , {
262+ libraryName,
263+ query,
264+ } ) ;
265+ return {
266+ content : [ {
267+ type : "text" ,
268+ text : responseText ,
269+ } ] ,
270+ } ;
271+ } catch ( error : any ) {
272+ return {
273+ isError : true ,
274+ content : [ { type : "text" , text : `resolve-library-id failed: ${ error . message } ` } ] ,
275+ } ;
276+ }
277+ }
278+ ) ;
279+
280+ server . tool (
281+ "query-docs" ,
282+ `
283+ Fetch context-aware docs snippets for a Context7 library.
284+ You must pass a Context7-compatible library ID, e.g. /reactjs/react.dev
285+ ` ,
286+ {
287+ libraryId : z . string ( ) . describe ( "Context7 library ID, e.g. /reactjs/react.dev" ) ,
288+ query : z . string ( ) . describe ( "The question or task to fetch relevant docs for" ) ,
289+ } ,
290+ async ( { libraryId, query } ) => {
291+ try {
292+ const responseText = await callContext7Api ( "context" , {
293+ libraryId : normaliseContext7LibraryId ( libraryId ) ,
294+ query,
295+ } ) ;
296+ return {
297+ content : [ {
298+ type : "text" ,
299+ text : responseText ,
300+ } ] ,
301+ } ;
302+ } catch ( error : any ) {
303+ return {
304+ isError : true ,
305+ content : [ { type : "text" , text : `query-docs failed: ${ error . message } ` } ] ,
306+ } ;
307+ }
308+ }
309+ ) ;
310+
311+ server . tool (
312+ "get-library-docs" ,
313+ `
314+ Legacy Context7-compatible docs tool.
315+ Fetch documentation for a Context7-compatible library ID.
316+ Use resolve-library-id first unless you already have an ID.
317+ ` ,
318+ {
319+ context7CompatibleLibraryID : z . string ( ) . describe ( "Context7-compatible library ID, e.g. /reactjs/react.dev" ) ,
320+ topic : z . string ( ) . default ( "" ) . describe ( "Optional topic to focus docs lookup" ) ,
321+ tokens : z . number ( ) . int ( ) . min ( 1 ) . default ( 10000 ) . describe ( "Retained for compatibility; currently ignored" ) ,
322+ page : z . number ( ) . int ( ) . min ( 1 ) . default ( 1 ) . describe ( "Retained for compatibility; currently ignored" ) ,
323+ } ,
324+ async ( { context7CompatibleLibraryID, topic } ) => {
325+ const requestedTopic = topic ?. trim ( ) ;
326+ const query = requestedTopic ? `${ requestedTopic } documentation` : "library documentation" ;
327+
328+ try {
329+ const responseText = await callContext7Api ( "context" , {
330+ libraryId : normaliseContext7LibraryId ( context7CompatibleLibraryID ) ,
331+ query,
332+ } ) ;
333+ return {
334+ content : [ {
335+ type : "text" ,
336+ text : responseText ,
337+ } ] ,
338+ } ;
339+ } catch ( error : any ) {
340+ return {
341+ isError : true ,
342+ content : [ { type : "text" , text : `get-library-docs failed: ${ error . message } ` } ] ,
343+ } ;
344+ }
345+ }
346+ ) ;
0 commit comments