1- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp .js" ;
1+ import { Server } from "@modelcontextprotocol/sdk/server/index .js" ;
22import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js" ;
3- import { z } from "zod" ;
4- import { ensureDocs , updateDocs , getDocsDir , initLocalDocs } from './cache.js' ;
3+ import {
4+ CallToolRequestSchema ,
5+ ListToolsRequestSchema ,
6+ ListResourcesRequestSchema ,
7+ ReadResourceRequestSchema
8+ } from "@modelcontextprotocol/sdk/types.js" ;
9+ import { ensureDocs , updateDocs , initLocalDocs } from './cache.js' ;
510import { searchDocs } from './tools/search.js' ;
611import { readDoc } from './tools/read.js' ;
712import { handleDocsResource } from './resources/docs.js' ;
813import path from 'path' ;
914import fs from 'fs' ;
10- import { fileURLToPath } from 'url' ;
1115
12- // Initialize server
13- export const server = new McpServer ( {
14- name : "baritone-docs-mcp" ,
15- version : "1.0.0" ,
16- } ) ;
17-
18- // -- Tools --
19-
20- // Tool: Refresh Docs
21- server . tool (
22- "baritone_refresh_docs" ,
23- "Download and update the local documentation cache from the GitHub repository." ,
16+ // Tool definitions
17+ const TOOLS = [
18+ {
19+ name : "baritone_refresh_docs" ,
20+ description : "Download and update the local documentation cache from the GitHub repository." ,
21+ inputSchema : {
22+ type : "object" ,
23+ properties : {
24+ force : {
25+ type : "boolean" ,
26+ description : "Force re-download even if cache exists"
27+ }
28+ }
29+ }
30+ } ,
2431 {
25- force : z . boolean ( ) . optional ( ) . describe ( "Force re-download even if cache exists" )
32+ name : "baritone_search_docs" ,
33+ description : "Search the Baritone documentation for a specific query." ,
34+ inputSchema : {
35+ type : "object" ,
36+ properties : {
37+ query : {
38+ type : "string" ,
39+ description : "The search query (e.g., 'GoalBlock', 'pathing')"
40+ }
41+ } ,
42+ required : [ "query" ]
43+ }
2644 } ,
27- async ( { force } ) => {
28- try {
29- await updateDocs ( ) ;
30- return {
31- content : [ { type : "text" , text : "Documentation updated successfully." } ]
32- } ;
33- } catch ( err : any ) {
34- return {
35- content : [ { type : "text" , text : `Failed to update docs: ${ err . message } ` } ] ,
36- isError : true
37- } ;
45+ {
46+ name : "baritone_read_doc" ,
47+ description : "Read the full content of a specific documentation file." ,
48+ inputSchema : {
49+ type : "object" ,
50+ properties : {
51+ path : {
52+ type : "string" ,
53+ description : "The relative path to the file (as returned by search results)"
54+ }
55+ } ,
56+ required : [ "path" ]
3857 }
3958 }
40- ) ;
59+ ] ;
4160
42- // Tool: Search Docs
43- server . tool (
44- "baritone_search_docs" ,
45- "Search the Baritone documentation for a specific query." ,
61+ // Resource definitions
62+ const RESOURCES = [
4663 {
47- query : z . string ( ) . describe ( "The search query (e.g., 'GoalBlock', 'pathing')" )
64+ uri : "docs://{path}" ,
65+ name : "Baritone Documentation" ,
66+ description : "Access Baritone documentation files" ,
67+ mimeType : "text/markdown"
68+ }
69+ ] ;
70+
71+ // Initialize server
72+ export const server = new Server (
73+ {
74+ name : "baritone-docs-mcp" ,
75+ version : "1.0.0" ,
4876 } ,
49- async ( { query } ) => {
50- if ( ! ensureDocs ( ) ) {
51- return {
52- content : [ { type : "text" , text : "Documentation not found. Please run baritone_refresh_docs first." } ] ,
53- isError : true
54- } ;
77+ {
78+ capabilities : {
79+ tools : { } ,
80+ resources : { }
5581 }
82+ }
83+ ) ;
5684
57- const results = await searchDocs ( query ) ;
85+ // -- Request Handlers --
5886
59- if ( results . length === 0 ) {
60- return {
61- content : [ { type : "text" , text : "No results found." } ]
62- } ;
63- }
87+ // List available tools
88+ server . setRequestHandler ( ListToolsRequestSchema , async ( ) => {
89+ return { tools : TOOLS } ;
90+ } ) ;
6491
65- const text = results . map ( r =>
66- `### [${ r . file . name } ](${ r . file . path } )\n` +
67- `**Score**: ${ r . score } \n` +
68- `**Matches**: \n${ r . matches ?. map ( m => `> ${ m } ` ) . join ( '\n' ) } \n`
69- ) . join ( '\n---\n' ) ;
92+ // Handle tool calls
93+ server . setRequestHandler ( CallToolRequestSchema , async ( request ) => {
94+ const { name, arguments : args } = request . params ;
95+
96+ try {
97+ switch ( name ) {
98+ case "baritone_refresh_docs" : {
99+ try {
100+ await updateDocs ( ) ;
101+ return {
102+ content : [ { type : "text" , text : "Documentation updated successfully." } ]
103+ } ;
104+ } catch ( err : any ) {
105+ return {
106+ content : [ { type : "text" , text : `Failed to update docs: ${ err . message } ` } ] ,
107+ isError : true
108+ } ;
109+ }
110+ }
70111
71- return {
72- content : [ { type : "text" , text } ]
73- } ;
74- }
75- ) ;
112+ case "baritone_search_docs" : {
113+ const query = ( args as any ) ?. query ;
114+
115+ if ( ! query ) {
116+ return {
117+ content : [ { type : "text" , text : "Query parameter is required." } ] ,
118+ isError : true
119+ } ;
120+ }
121+
122+ if ( ! ensureDocs ( ) ) {
123+ return {
124+ content : [ { type : "text" , text : "Documentation not found. Please run baritone_refresh_docs first." } ] ,
125+ isError : true
126+ } ;
127+ }
128+
129+ const results = await searchDocs ( query ) ;
130+
131+ if ( results . length === 0 ) {
132+ return {
133+ content : [ { type : "text" , text : "No results found." } ]
134+ } ;
135+ }
136+
137+ const text = results . map ( r =>
138+ `### [${ r . file . name } ](${ r . file . path } )\n` +
139+ `**Score**: ${ r . score } \n` +
140+ `**Matches**: \n${ r . matches ?. map ( m => `> ${ m } ` ) . join ( '\n' ) } \n`
141+ ) . join ( '\n---\n' ) ;
76142
77- // Tool: Read Doc
78- server . tool (
79- "baritone_read_doc" ,
80- "Read the full content of a specific documentation file." ,
81- {
82- path : z . string ( ) . describe ( "The relative path to the file (as returned by search results)" )
83- } ,
84- async ( { path } ) => {
85- if ( ! ensureDocs ( ) ) {
86- return {
87- content : [ { type : "text" , text : "Documentation not found. Please run baritone_refresh_docs first." } ] ,
88- isError : true
89- } ;
90- }
143+ return {
144+ content : [ { type : "text" , text } ]
145+ } ;
146+ }
91147
92- try {
93- const content = readDoc ( path ) ;
94- if ( content === null ) {
148+ case "baritone_read_doc" : {
149+ const docPath = ( args as any ) ?. path ;
150+
151+ if ( ! docPath ) {
152+ return {
153+ content : [ { type : "text" , text : "Path parameter is required." } ] ,
154+ isError : true
155+ } ;
156+ }
157+
158+ if ( ! ensureDocs ( ) ) {
159+ return {
160+ content : [ { type : "text" , text : "Documentation not found. Please run baritone_refresh_docs first." } ] ,
161+ isError : true
162+ } ;
163+ }
164+
165+ try {
166+ const content = readDoc ( docPath ) ;
167+ if ( content === null ) {
168+ return {
169+ content : [ { type : "text" , text : "File not found." } ] ,
170+ isError : true
171+ } ;
172+ }
173+ return {
174+ content : [ { type : "text" , text : content } ]
175+ } ;
176+ } catch ( err : any ) {
177+ return {
178+ content : [ { type : "text" , text : `Error reading file: ${ err . message } ` } ] ,
179+ isError : true
180+ } ;
181+ }
182+ }
183+
184+ default :
95185 return {
96- content : [ { type : "text" , text : "File not found." } ] ,
186+ content : [ { type : "text" , text : `Unknown tool: ${ name } ` } ] ,
97187 isError : true
98188 } ;
99- }
100- return {
101- content : [ { type : "text" , text : content } ]
102- } ;
103- } catch ( err : any ) {
104- return {
105- content : [ { type : "text" , text : `Error reading file: ${ err . message } ` } ] ,
106- isError : true
107- } ;
108189 }
190+ } catch ( error : any ) {
191+ return {
192+ content : [ { type : "text" , text : `Error: ${ error . message } ` } ] ,
193+ isError : true
194+ } ;
109195 }
110- ) ;
196+ } ) ;
111197
112- // Resource: Docs
113- server . resource (
114- "docs" ,
115- "docs://{path}" ,
116- async ( uri ) => {
117- return handleDocsResource ( uri ) ;
118- }
119- ) ;
198+ // List available resources
199+ server . setRequestHandler ( ListResourcesRequestSchema , async ( ) => {
200+ return { resources : RESOURCES } ;
201+ } ) ;
202+
203+ // Handle resource reads
204+ server . setRequestHandler ( ReadResourceRequestSchema , async ( request ) => {
205+ const uri = new URL ( request . params . uri ) ;
206+ return handleDocsResource ( uri ) ;
207+ } ) ;
120208
121209// -- Setup --
122210
@@ -140,12 +228,8 @@ export async function main() {
140228 await server . connect ( transport ) ;
141229}
142230
143- // Only run main if executed directly (ESM entry point check)
144- const isMainModule = import . meta. url === `file://${ process . argv [ 1 ] } ` ||
145- import . meta. url . endsWith ( process . argv [ 1 ] ) ;
146-
147- if ( isMainModule ) {
148- main ( ) . catch ( ( error ) => {
149- process . exit ( 1 ) ;
150- } ) ;
151- }
231+ // Start the MCP server
232+ main ( ) . catch ( ( error ) => {
233+ console . error ( 'Fatal error:' , error ) ;
234+ process . exit ( 1 ) ;
235+ } ) ;
0 commit comments