Skip to content

Commit 6fe825d

Browse files
authored
Merge pull request #3 from QuantGeekDev/feature/automatic-resource-detection
featL add resource autodiscovery
2 parents 0e6a21b + 7a36535 commit 6fe825d

File tree

2 files changed

+85
-31
lines changed

2 files changed

+85
-31
lines changed

src/core/MCPServer.ts

+64-28
Original file line numberDiff line numberDiff line change
@@ -13,45 +13,68 @@ import { logger } from "./Logger.js";
1313
export interface MCPServerConfig {
1414
name?: string;
1515
version?: string;
16+
basePath?: string;
1617
}
1718

19+
export type ServerCapabilities = {
20+
tools?: {
21+
enabled: true;
22+
};
23+
schemas?: {
24+
enabled: true;
25+
};
26+
prompts?: {
27+
enabled: true;
28+
};
29+
};
30+
1831
export class MCPServer {
1932
private server: Server;
2033
private toolsMap: Map<string, ToolProtocol> = new Map();
2134
private toolLoader: ToolLoader;
2235
private serverName: string;
2336
private serverVersion: string;
37+
private basePath: string;
2438

2539
constructor(config: MCPServerConfig = {}) {
40+
this.basePath = this.resolveBasePath(config.basePath);
2641
this.serverName = config.name ?? this.getDefaultName();
2742
this.serverVersion = config.version ?? this.getDefaultVersion();
2843

2944
logger.info(
3045
`Initializing MCP Server: ${this.serverName}@${this.serverVersion}`
3146
);
3247

48+
this.toolLoader = new ToolLoader(this.basePath);
49+
3350
this.server = new Server(
3451
{
3552
name: this.serverName,
3653
version: this.serverVersion,
3754
},
3855
{
3956
capabilities: {
40-
tools: {
41-
enabled: true,
42-
},
57+
tools: { enabled: true },
4358
},
4459
}
4560
);
4661

47-
this.toolLoader = new ToolLoader();
4862
this.setupHandlers();
4963
}
5064

65+
private resolveBasePath(configPath?: string): string {
66+
if (configPath) {
67+
return configPath;
68+
}
69+
if (process.argv[1]) {
70+
return process.argv[1];
71+
}
72+
return process.cwd();
73+
}
74+
5175
private readPackageJson(): any {
5276
try {
53-
const mainModulePath = process.argv[1];
54-
const packagePath = join(dirname(mainModulePath), "..", "package.json");
77+
const packagePath = join(dirname(this.basePath), "package.json");
5578
const packageContent = readFileSync(packagePath, "utf-8");
5679
const packageJson = JSON.parse(packageContent);
5780
logger.debug(`Successfully read package.json from: ${packagePath}`);
@@ -63,27 +86,19 @@ export class MCPServer {
6386
}
6487

6588
private getDefaultName(): string {
66-
try {
67-
const packageJson = this.readPackageJson();
68-
if (packageJson?.name) {
69-
logger.info(`Using name from package.json: ${packageJson.name}`);
70-
return packageJson.name;
71-
}
72-
} catch (error) {
73-
logger.warn(`Error getting name from package.json: ${error}`);
89+
const packageJson = this.readPackageJson();
90+
if (packageJson?.name) {
91+
logger.info(`Using name from package.json: ${packageJson.name}`);
92+
return packageJson.name;
7493
}
7594
return "unnamed-mcp-server";
7695
}
7796

7897
private getDefaultVersion(): string {
79-
try {
80-
const packageJson = this.readPackageJson();
81-
if (packageJson?.version) {
82-
logger.info(`Using version from package.json: ${packageJson.version}`);
83-
return packageJson.version;
84-
}
85-
} catch (error) {
86-
logger.warn(`Error getting version from package.json: ${error}`);
98+
const packageJson = this.readPackageJson();
99+
if (packageJson?.version) {
100+
logger.info(`Using version from package.json: ${packageJson.version}`);
101+
return packageJson.version;
87102
}
88103
return "0.0.0";
89104
}
@@ -116,6 +131,21 @@ export class MCPServer {
116131
});
117132
}
118133

134+
private async detectCapabilities(): Promise<ServerCapabilities> {
135+
const capabilities: ServerCapabilities = {};
136+
137+
//IK this is unecessary but it'll guide future schema and prompt capability autodiscovery
138+
139+
if (await this.toolLoader.hasTools()) {
140+
capabilities.tools = { enabled: true };
141+
logger.debug("Tools capability enabled");
142+
} else {
143+
logger.debug("No tools found, tools capability disabled");
144+
}
145+
146+
return capabilities;
147+
}
148+
119149
async start() {
120150
try {
121151
const tools = await this.toolLoader.loadTools();
@@ -126,12 +156,18 @@ export class MCPServer {
126156
const transport = new StdioServerTransport();
127157
await this.server.connect(transport);
128158

129-
logger.info(
130-
`Started ${this.serverName}@${this.serverVersion} with ${tools.length} tools`
131-
);
132-
logger.info(
133-
`Available tools: ${Array.from(this.toolsMap.keys()).join(", ")}`
134-
);
159+
if (tools.length > 0) {
160+
logger.info(
161+
`Started ${this.serverName}@${this.serverVersion} with ${tools.length} tools`
162+
);
163+
logger.info(
164+
`Available tools: ${Array.from(this.toolsMap.keys()).join(", ")}`
165+
);
166+
} else {
167+
logger.info(
168+
`Started ${this.serverName}@${this.serverVersion} with no tools`
169+
);
170+
}
135171
} catch (error) {
136172
logger.error(`Server initialization error: ${error}`);
137173
throw error;

src/core/toolLoader.ts

+21-3
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,30 @@ export class ToolLoader {
77
private readonly TOOLS_DIR: string;
88
private readonly EXCLUDED_FILES = ["BaseTool.js", "*.test.js", "*.spec.js"];
99

10-
constructor() {
11-
const mainModulePath = process.argv[1];
10+
constructor(basePath?: string) {
11+
const mainModulePath = basePath || process.argv[1];
1212
this.TOOLS_DIR = join(dirname(mainModulePath), "tools");
1313
logger.debug(`Initialized ToolLoader with directory: ${this.TOOLS_DIR}`);
1414
}
1515

16+
async hasTools(): Promise<boolean> {
17+
try {
18+
const stats = await fs.stat(this.TOOLS_DIR);
19+
if (!stats.isDirectory()) {
20+
logger.debug("Tools path exists but is not a directory");
21+
return false;
22+
}
23+
24+
const files = await fs.readdir(this.TOOLS_DIR);
25+
const hasValidFiles = files.some((file) => this.isToolFile(file));
26+
logger.debug(`Tools directory has valid files: ${hasValidFiles}`);
27+
return hasValidFiles;
28+
} catch (error) {
29+
logger.debug("No tools directory found");
30+
return false;
31+
}
32+
}
33+
1634
private isToolFile(file: string): boolean {
1735
if (!file.endsWith(".js")) return false;
1836
const isExcluded = this.EXCLUDED_FILES.some((pattern) => {
@@ -54,7 +72,7 @@ export class ToolLoader {
5472
try {
5573
stats = await fs.stat(this.TOOLS_DIR);
5674
} catch (error) {
57-
logger.error(`Error accessing tools directory: ${error}`);
75+
logger.debug("No tools directory found");
5876
return [];
5977
}
6078

0 commit comments

Comments
 (0)