Skip to content

Commit 0299c49

Browse files
committed
fix: typescript
1 parent 63e581e commit 0299c49

File tree

11 files changed

+163
-49
lines changed

11 files changed

+163
-49
lines changed

src/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { setupProcessErrorHandlers } from './utils/error-handlers.ts'
55

66
const logger = initLogger()
77

8-
export async function startServer () {
8+
export async function startServer (): Promise<void> {
99
let server
1010
try {
1111
server = await createMcpServer()

src/prompts/index.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,30 @@
1+
import type { Server } from '@modelcontextprotocol/sdk/server/index.js'
12
import {
23
GetPromptRequestSchema,
34
ListPromptsRequestSchema,
45
} from '@modelcontextprotocol/sdk/types.js'
56

6-
import { initLogger } from '../utils/logger.ts'
7+
import { initLogger, type Logger } from '../utils/logger.ts'
78

8-
const logger = initLogger()
9+
const logger: Logger = initLogger()
910

10-
export async function initializePrompts (server) {
11+
interface PromptsDictionary {
12+
[name: string]: {
13+
name: string
14+
description: string
15+
arguments: Array<{
16+
name: string
17+
description: string
18+
required: boolean
19+
}>
20+
handler: (args: Record<string, any>) => Promise<{ messages: Array<{ role: string; content: { type: string; text: string } }> }>
21+
}
22+
}
23+
24+
export async function initializePrompts (server: Server): Promise<void> {
1125
logger.info({ msg: 'Initializing prompts...' })
1226

13-
const prompts = {
27+
const prompts: PromptsDictionary = {
1428
'nodejs-api-lookup': {
1529
name: 'nodejs-api-lookup',
1630
description:
@@ -91,7 +105,7 @@ export async function initializePrompts (server) {
91105
// eslint-disable-next-line security/detect-object-injection
92106
const prompt = prompts[name]
93107
if (prompt) {
94-
return await prompt.handler(args)
108+
return await prompt.handler(args || {})
95109
}
96110

97111
throw new Error('Prompt not found')

src/resources/index.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
import type { Server } from '@modelcontextprotocol/sdk/server/index.js'
12
import {
23
ListResourcesRequestSchema,
34
ReadResourceRequestSchema,
45
} from '@modelcontextprotocol/sdk/types.js'
5-
import { initLogger } from '../utils/logger.ts'
6+
import { initLogger, type Logger } from '../utils/logger.ts'
7+
import type { ReadResourceRequest } from '@modelcontextprotocol/sdk/types.js'
68

7-
const logger = initLogger()
9+
const logger: Logger = initLogger()
810

9-
export async function initializeResources (server) {
11+
export async function initializeResources (server: Server): Promise<void> {
1012
logger.info({ msg: 'Initializing resources...' })
1113

1214
const resourceNodejsReleasesChartURL =
@@ -33,7 +35,7 @@ export async function initializeResources (server) {
3335
name: 'Node.js Releases Schedule Chart',
3436
description: 'A chart showing the release schedule of Node.js versions.',
3537
mimeType: 'image/svg+xml',
36-
handler: async (request) => {
38+
handler: async (request: ReadResourceRequest) => {
3739
logger.info({ msg: 'Resource URI Access:', uri: request.params.uri })
3840

3941
return {

src/server/server.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
2-
import { initLogger } from '../utils/logger.ts'
2+
import { initLogger, type Logger } from '../utils/logger.ts'
33
import { initializeResources } from '../resources/index.ts'
44
import { initializePrompts } from '../prompts/index.ts'
55
import { initializeTools } from '../tools/index.ts'
66

7-
const logger = initLogger()
7+
const logger: Logger = initLogger()
8+
9+
export async function createMcpServer (): Promise<Server> {
10+
const serverInfo = {
11+
name: 'nodejs-module-api-documentation',
12+
description:
13+
'Search built-in core Node.js modules API Documentation. Use whenever the user asks questions about Node.js API, Node.js modules or Node.js functions.',
14+
version: '1.0.0',
15+
}
816

9-
export async function createMcpServer () {
1017
const server = new Server(
11-
{
12-
name: 'nodejs-module-api-documentation',
13-
description:
14-
'Search built-in core Node.js modules API Documentation. Use whenever the user asks questions about Node.js API, Node.js modules or Node.js functions.',
15-
version: '1.0.0',
16-
},
18+
serverInfo,
1719
{
1820
capabilities: {
1921
resources: {},
@@ -29,8 +31,8 @@ export async function createMcpServer () {
2931

3032
logger.info({
3133
msg: 'MCP Server instance created',
32-
name: server.name,
33-
version: server.version,
34+
name: serverInfo.name,
35+
version: serverInfo.version,
3436
})
3537

3638
return server

src/services/api-docs-service.ts

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,66 @@
1-
import { initLogger } from '../utils/logger.ts'
1+
import { initLogger, type Logger } from '../utils/logger.ts'
22
import { DocsFormatter } from './docs-formatter-service.ts'
33

4+
// Type definitions for Node.js API documentation structure
5+
interface ApiMethod {
6+
textRaw: string
7+
desc?: string
8+
name?: string
9+
classes?: ApiClass[]
10+
methods?: ApiMethod[]
11+
}
12+
13+
interface ApiClass {
14+
textRaw: string
15+
desc?: string
16+
name?: string
17+
methods?: ApiMethod[]
18+
}
19+
20+
interface ApiModule {
21+
textRaw: string
22+
displayName?: string
23+
name: string
24+
desc?: string
25+
classes?: ApiClass[]
26+
methods?: ApiMethod[]
27+
modules?: ApiModule[]
28+
}
29+
30+
interface ApiDocsData {
31+
modules: ApiModule[]
32+
}
33+
34+
interface ModulesData {
35+
modules: ApiModule[]
36+
}
37+
38+
interface FormattingOptions {
39+
class?: string
40+
method?: string
41+
}
42+
443
export class ApiDocsService {
44+
private logger: Logger
45+
private docsFormatter: DocsFormatter
46+
private url: string
47+
private modulesData: ModulesData | null
48+
549
constructor () {
650
this.logger = initLogger()
751
this.docsFormatter = new DocsFormatter()
852
this.url = 'https://nodejs.org/docs/latest/api/all.json'
953
this.modulesData = null
1054
}
1155

12-
async fetchNodeApiDocs () {
56+
async fetchNodeApiDocs (): Promise<ApiDocsData> {
1357
this.logger.info({ msg: 'Fetching Node.js API documentation...' })
1458
try {
1559
const response = await fetch(this.url)
1660
if (!response.ok) {
1761
throw new Error(`HTTP error! status: ${response.status} ${response.statusText}`)
1862
}
19-
const data = await response.json()
63+
const data = await response.json() as ApiDocsData
2064
this.logger.info({ msg: 'Successfully fetched Node.js API documentation', url: this.url })
2165
return data
2266
} catch (error) {
@@ -25,11 +69,11 @@ export class ApiDocsService {
2569
}
2670
}
2771

28-
normalizeModuleName (name) {
72+
normalizeModuleName (name: string): string {
2973
return this.docsFormatter.normalizeModuleName(name)
3074
}
3175

32-
async getApiDocsModules () {
76+
async getApiDocsModules (): Promise<ModulesData> {
3377
if (this.modulesData) {
3478
// return from cached data
3579
return this.modulesData
@@ -40,7 +84,8 @@ export class ApiDocsService {
4084
// Remove entries without Class or Method
4185
const originalCount = apiDocs.modules?.length
4286
apiDocs.modules = apiDocs.modules.filter(module =>
43-
module?.classes?.length > 0 || module?.methods?.length > 0
87+
module?.classes?.length && module.classes.length > 0 ||
88+
module?.methods?.length && module.methods.length > 0
4489
)
4590
this.logger.info({ msg: `Modules count: ${originalCount}` })
4691

@@ -52,15 +97,15 @@ export class ApiDocsService {
5297
return this.modulesData
5398
}
5499

55-
async getFormattedModuleDoc (moduleData, options = {}) {
100+
async getFormattedModuleDoc (moduleData: ApiModule, options: FormattingOptions = {}): Promise<string> {
56101
if (!moduleData) {
57102
return ''
58103
}
59104

60105
return this.docsFormatter.createModuleDocumentation(moduleData, options)
61106
}
62107

63-
async getFormattedModuleSummary (moduleData) {
108+
async getFormattedModuleSummary (moduleData: ApiModule): Promise<string> {
64109
if (!moduleData) {
65110
return ''
66111
}

src/services/docs-formatter-service.ts

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,34 @@
1+
// Type definitions for Node.js API documentation structure
2+
interface ApiMethod {
3+
textRaw: string
4+
desc?: string
5+
name?: string
6+
classes?: ApiClass[]
7+
methods?: ApiMethod[]
8+
}
9+
10+
interface ApiClass {
11+
textRaw: string
12+
desc?: string
13+
name?: string
14+
methods?: ApiMethod[]
15+
}
16+
17+
interface ApiModule {
18+
textRaw: string
19+
displayName?: string
20+
name: string
21+
desc?: string
22+
classes?: ApiClass[]
23+
methods?: ApiMethod[]
24+
modules?: ApiModule[]
25+
}
26+
27+
interface FormattingOptions {
28+
class?: string
29+
method?: string
30+
}
31+
132
/**
233
* Service responsible for formatting Node.js API documentation into readable markdown content
334
*/
@@ -10,20 +41,20 @@ export class DocsFormatter {
1041
/**
1142
* Formats content by adding extra newlines for better markdown rendering
1243
*/
13-
formatContent (content) {
44+
formatContent (content: string): string {
1445
if (!content) return ''
1546
return content.replace(/\n/g, '\n\n')
1647
}
1748

1849
/**
1950
* Normalizes a module name for use as a tool identifier
2051
*/
21-
normalizeModuleName (name) {
52+
normalizeModuleName (name: string): string {
2253
const toolName = `get_api_for-${name.toLowerCase().replace(/[^a-zA-Z0-9_-]/g, '')}`
2354
return toolName.length > 64 ? toolName.slice(0, 63) : toolName
2455
}
2556

26-
formatModuleSummary (module) {
57+
formatModuleSummary (module: ApiModule): string {
2758
let content = `## ${module.displayName || module.textRaw} (${module.name})\n`
2859

2960
if (
@@ -48,7 +79,7 @@ export class DocsFormatter {
4879
return content + '\n'
4980
}
5081

51-
formatItems (items, title, query) {
82+
formatItems (items: ApiMethod[] | ApiClass[] | ApiModule[], title: string, query?: string): string {
5283
if (!items || items.length === 0) return ''
5384

5485
let sectionContent = ''
@@ -72,7 +103,7 @@ export class DocsFormatter {
72103

73104
// Phase 2, we dive deeper into nested methods inside module?.modules?.methods
74105
items.forEach((submodule) => {
75-
if (submodule?.methods) {
106+
if ('methods' in submodule && submodule?.methods) {
76107
sectionContent += `### ${submodule.textRaw} Methods\n\n`
77108
submodule.methods.forEach((submethod) => {
78109
sectionContent += `#### ${submethod.textRaw}\n`
@@ -84,16 +115,16 @@ export class DocsFormatter {
84115
return sectionContent
85116
}
86117

87-
createModuleDocumentation (module, { class: classQuery, method: methodQuery } = {}) {
118+
createModuleDocumentation (module: ApiModule, { class: classQuery, method: methodQuery }: FormattingOptions = {}): string {
88119
let content = `# ${module.textRaw}\n\n`
89120

90121
if (module.desc) {
91122
content += `## Description\n${this.formatContent(module.desc)}\n\n`
92123
}
93124

94-
content += this.formatItems(module.classes, 'Classes', classQuery)
95-
content += this.formatItems(module.methods, 'Methods', methodQuery)
96-
content += this.formatItems(module.modules, 'Submodules', methodQuery)
125+
content += this.formatItems(module.classes || [], 'Classes', classQuery)
126+
content += this.formatItems(module.methods || [], 'Methods', methodQuery)
127+
content += this.formatItems(module.modules || [], 'Submodules', methodQuery)
97128

98129
return content
99130
}

src/tools/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
import type { Server } from '@modelcontextprotocol/sdk/server/index.js'
12
import {
23
ListToolsRequestSchema,
34
CallToolRequestSchema,
45
} from '@modelcontextprotocol/sdk/types.js'
56

67
import { createSearchTool, createModuleTools } from './tools-factory.ts'
78

8-
export async function initializeTools (server) {
9+
export async function initializeTools (server: Server): Promise<void> {
910
// Create the search tool
1011
const searchTool = await createSearchTool()
1112

@@ -40,6 +41,6 @@ export async function initializeTools (server) {
4041
}
4142
const tool = tools[request.params.name]
4243

43-
return await tool.handler(request.params.arguments)
44+
return await tool.handler(request.params.arguments || {})
4445
})
4546
}

src/tools/tools-factory.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
1-
import { initLogger } from '../utils/logger.ts'
1+
import { initLogger, type Logger } from '../utils/logger.ts'
22
import { ApiDocsService } from '../services/api-docs-service.ts'
33

4-
const logger = initLogger()
4+
const logger: Logger = initLogger()
55
const apiDocsService = new ApiDocsService()
66

7-
export async function createSearchTool () {
7+
interface ToolDefinition {
8+
name: string
9+
description: string
10+
inputSchema: {
11+
type: string
12+
properties: Record<string, any>
13+
}
14+
handler: (params: Record<string, any>) => Promise<{ content: { type: string; text: string }[] }>
15+
}
16+
17+
interface ToolsDictionary {
18+
[key: string]: ToolDefinition
19+
}
20+
21+
export async function createSearchTool (): Promise<ToolDefinition> {
822
const { modules } = await apiDocsService.getApiDocsModules()
923

1024
return {
@@ -15,7 +29,7 @@ export async function createSearchTool () {
1529
type: 'object',
1630
properties: {},
1731
},
18-
async handler (args) {
32+
async handler () {
1933
logger.info({
2034
msg: 'Tool execution started: search-nodejs-modules-api-documentation',
2135
})
@@ -31,7 +45,7 @@ export async function createSearchTool () {
3145
}
3246

3347
export async function createModuleTools () {
34-
const tools = {}
48+
const tools: ToolsDictionary = {}
3549
const { modules } = await apiDocsService.getApiDocsModules()
3650

3751
modules.forEach((module) => {

0 commit comments

Comments
 (0)