Skip to content

Commit 5a5851f

Browse files
committed
add npx support
1 parent 94423d4 commit 5a5851f

File tree

6 files changed

+100
-29
lines changed

6 files changed

+100
-29
lines changed

bin/sensei-mcp.js

100644100755
+9-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
#!/usr/bin/env node
22

3-
// Use CommonJS require for better compatibility with npx
4-
const path = require('path');
5-
const { spawn } = require('child_process');
3+
// Use ES modules since package.json has "type": "module"
4+
import { fileURLToPath } from 'url';
5+
import { dirname, join } from 'path';
6+
import { spawn } from 'child_process';
7+
8+
// Get the directory of the current module
9+
const __filename = fileURLToPath(import.meta.url);
10+
const __dirname = dirname(__filename);
611

712
// Path to the built main.js file
8-
const mainPath = path.join(__dirname, '../build/src/main.js');
13+
const mainPath = join(__dirname, '../build/src/main.js');
914

1015
// Log startup information
1116
console.log('Starting Sensei MCP server...');

eslint.config.mjs

+11
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,15 @@ export default tseslint.config(
6363
},
6464
},
6565
},
66+
// Configuration for bin directory
67+
{
68+
files: ['bin/**/*.js'],
69+
languageOptions: {
70+
globals: {
71+
...globals.node,
72+
console: 'readonly',
73+
process: 'readonly',
74+
},
75+
},
76+
},
6677
);

package.json

+8-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
"bin": {
77
"sensei-mcp": "./bin/sensei-mcp.js"
88
},
9+
"files": [
10+
"bin/",
11+
"build/",
12+
"prompts/",
13+
"resources/"
14+
],
915
"devDependencies": {
1016
"@eslint/js": "~9.17",
1117
"@types/eslint__js": "~8.42",
@@ -39,8 +45,8 @@
3945
"prettier": "prettier \"{src,__{tests}__}/**/*.{ts,mts}\" --config .prettierrc --write",
4046
"prettier:check": "prettier \"{src,__{tests}__}/**/*.{ts,mts}\" --config .prettierrc --check",
4147
"test:watch": "vitest unit",
42-
"prepare": "npm run build",
43-
"postinstall": "npm run prepare"
48+
"prepare": "npm run build && chmod +x bin/sensei-mcp.js",
49+
"postinstall": "chmod +x bin/sensei-mcp.js"
4450
},
4551
"license": "Apache-2.0",
4652
"dependencies": {

src/prompts.ts

+27-5
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,30 @@ import {
88
CallToolResult,
99
GetPromptResult,
1010
} from '@modelcontextprotocol/sdk/types.js';
11+
import { fileURLToPath } from 'url';
12+
13+
// Get the directory of the current module
14+
const __filename = fileURLToPath(import.meta.url);
15+
const __dirname = path.dirname(__filename);
1116

1217
// Configuration
18+
// Use the package directory if it exists, otherwise use the current working directory
1319
export const PROMPTS_DIR = path.join(process.cwd(), 'prompts');
1420

21+
// Try to use the package directory for prompts if it exists
22+
export async function getPromptsDir(): Promise<string> {
23+
const packagePromptsDir = path.join(__dirname, '../../prompts');
24+
try {
25+
await fs.access(packagePromptsDir);
26+
return packagePromptsDir;
27+
} catch (
28+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
29+
_error
30+
) {
31+
return PROMPTS_DIR;
32+
}
33+
}
34+
1535
// Resource reference regex pattern (e.g., {{resource:path/to/resource}})
1636
export const RESOURCE_REF_PATTERN = /\{\{resource:(.*?)\}\}/g;
1737

@@ -299,12 +319,14 @@ export async function loadPrompts(
299319
const span = Logger.span('loadPrompts');
300320

301321
try {
302-
await fs.mkdir(PROMPTS_DIR, { recursive: true });
303-
Logger.debug(`Ensuring prompts directory exists`, { path: PROMPTS_DIR });
322+
// Get the prompts directory
323+
const promptsDir = await getPromptsDir();
324+
await fs.mkdir(promptsDir, { recursive: true });
325+
Logger.debug(`Ensuring prompts directory exists`, { path: promptsDir });
304326

305-
const files = await fs.readdir(PROMPTS_DIR);
327+
const files = await fs.readdir(promptsDir);
306328
Logger.info(`Found ${files.length} potential prompt files`, {
307-
directory: PROMPTS_DIR,
329+
directory: promptsDir,
308330
});
309331

310332
let loadedCount = 0;
@@ -319,7 +341,7 @@ export async function loadPrompts(
319341
continue;
320342
}
321343

322-
const filePath = path.join(PROMPTS_DIR, file);
344+
const filePath = path.join(promptsDir, file);
323345
const stats = await fs.stat(filePath);
324346

325347
if (stats.isDirectory()) {

src/resources.ts

+40-16
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,29 @@ import {
55
ResourceTemplate,
66
} from '@modelcontextprotocol/sdk/server/mcp.js';
77
import Logger from './logger.js';
8+
import { fileURLToPath } from 'url';
9+
10+
// Get the directory of the current module
11+
const __filename = fileURLToPath(import.meta.url);
12+
const __dirname = path.dirname(__filename);
813

914
// Configuration
1015
export const RESOURCES_DIR = path.join(process.cwd(), 'resources');
1116

17+
// Try to use the package directory for resources if it exists
18+
export async function getResourcesDir(): Promise<string> {
19+
const packageResourcesDir = path.join(__dirname, '../../resources');
20+
try {
21+
await fs.access(packageResourcesDir);
22+
return packageResourcesDir;
23+
} catch (
24+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
25+
_error
26+
) {
27+
return RESOURCES_DIR;
28+
}
29+
}
30+
1231
/**
1332
* Load a file's content from disk
1433
*/
@@ -20,9 +39,9 @@ export async function loadFile(filePath: string): Promise<string> {
2039
span.end('success');
2140
return content;
2241
} catch (error) {
23-
Logger.error(`Failed to load file: ${filePath}`, error);
42+
Logger.error(`Failed to load file`, error, { path: filePath });
2443
span.end('error');
25-
throw new Error(`Failed to load file: ${filePath}`);
44+
throw error;
2645
}
2746
}
2847

@@ -36,34 +55,39 @@ export async function loadResources(
3655
const resourceMap = new Map<string, string>();
3756

3857
try {
39-
await fs.mkdir(RESOURCES_DIR, { recursive: true });
40-
Logger.debug(`Ensuring resources directory exists`, {
41-
path: RESOURCES_DIR,
42-
});
58+
// Get the resources directory
59+
const resourcesDir = await getResourcesDir();
60+
await fs.mkdir(resourcesDir, { recursive: true });
61+
Logger.debug(`Ensuring resources directory exists`, { path: resourcesDir });
4362

44-
const files = await fs.readdir(RESOURCES_DIR, { recursive: true });
63+
const files = await fs.readdir(resourcesDir);
4564
Logger.info(`Found ${files.length} potential resource files`, {
46-
directory: RESOURCES_DIR,
65+
directory: resourcesDir,
4766
});
4867

4968
let loadedCount = 0;
5069

5170
for (const file of files) {
52-
const fileSpan = Logger.span('processResourceFile', { file });
71+
const resourceSpan = Logger.span('processResourceFile', { file });
72+
73+
if (!file.endsWith('.txt')) {
74+
Logger.trace(`Skipping non-txt file`, { file });
75+
resourceSpan.end('skipped_non_txt');
76+
continue;
77+
}
5378

54-
// Skip directories and non-text files
55-
const filePath = path.join(RESOURCES_DIR, file);
79+
const filePath = path.join(resourcesDir, file);
5680
const stats = await fs.stat(filePath);
5781

5882
if (stats.isDirectory()) {
5983
Logger.trace(`Skipping directory`, { path: filePath });
60-
fileSpan.end('skipped_directory');
84+
resourceSpan.end('skipped_directory');
6185
continue;
6286
}
6387

6488
// Convert file path to resource URI
6589
// e.g., resources/docs/intro.txt -> docs/intro
66-
const relPath = path.relative(RESOURCES_DIR, filePath);
90+
const relPath = path.relative(resourcesDir, filePath);
6791
const resourcePath = relPath.replace(/\.[^/.]+$/, ''); // Remove extension
6892
const resourceUri = `file://${resourcePath}`;
6993

@@ -91,12 +115,12 @@ export async function loadResources(
91115
uri: resourceUri,
92116
size: content.length,
93117
});
94-
fileSpan.end('success');
118+
resourceSpan.end('success');
95119
} catch (error) {
96120
Logger.error(`Failed to process resource file`, error, {
97121
path: filePath,
98122
});
99-
fileSpan.end('error');
123+
resourceSpan.end('error');
100124
}
101125
}
102126

@@ -110,7 +134,7 @@ export async function loadResources(
110134
const span = Logger.span('dynamicResourceLoad', { path: params.path });
111135
try {
112136
const resourcePath = params.path;
113-
const filePath = path.join(RESOURCES_DIR, resourcePath + '.txt');
137+
const filePath = path.join(resourcesDir, resourcePath + '.txt');
114138
const content = await loadFile(filePath);
115139
span.end('success');
116140
return {

src/server.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { loadResources } from './resources.js';
55
import { loadPrompts } from './prompts.js';
66
import fs from 'fs/promises';
77
import path from 'path';
8-
import { PROMPTS_DIR, parseMetadata } from './prompts.js';
8+
import { getPromptsDir, parseMetadata } from './prompts.js';
99

1010
// Server configuration
1111
const SERVER_CONFIG = {
@@ -20,8 +20,11 @@ export async function startServer(): Promise<void> {
2020
const span = Logger.span('startServer');
2121

2222
try {
23+
// Get the prompts directory
24+
const promptsDir = await getPromptsDir();
25+
2326
// Load the sensei prompt content to use as server instructions
24-
const senseiPromptPath = path.join(PROMPTS_DIR, 'sensei.txt');
27+
const senseiPromptPath = path.join(promptsDir, 'sensei.txt');
2528
let senseiInstructions = '';
2629

2730
try {

0 commit comments

Comments
 (0)