Skip to content

Commit cb27c0e

Browse files
committed
Refactor imports to use .js extensions and update tool registration logic
- Updated import statements across various files to include .js extensions for consistency. - Refactored the ToolLoader class to register tools directly from static imports instead of loading from file paths. - Removed obsolete test files related to tool loading and metadata validation. - Enhanced the tools manifest with new entries for additional tools. - Updated TypeScript configuration to use NodeNext module resolution. - Added new dependencies for esbuild and rimraf in yarn.lock.
1 parent b0ca4cc commit cb27c0e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+989
-590
lines changed

.eslintrc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"rules": {
2828
"@typescript-eslint/no-explicit-any": "error",
2929
"@typescript-eslint/explicit-function-return-type": "error",
30-
"@typescript-eslint/strict-boolean-expressions": "warn",
30+
"@typescript-eslint/strict-boolean-expressions": "off",
3131
"@typescript-eslint/no-unused-vars": [
3232
"error",
3333
{

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,15 @@ The server requires environment variables for Backstage API access:
6969
### Required Environment Variables
7070

7171
- `BACKSTAGE_BASE_URL` - Base URL of your Backstage instance (e.g., `https://backstage.example.com`)
72-
- `BACKSTAGE_TOKEN` - Authentication token for API access (optional, depending on your Backstage setup)
72+
73+
### Authentication Configuration
74+
75+
Choose one of the following authentication methods:
76+
77+
- `BACKSTAGE_TOKEN` - Bearer token for API access
78+
- `BACKSTAGE_CLIENT_ID`, `BACKSTAGE_CLIENT_SECRET`, `BACKSTAGE_TOKEN_URL` - OAuth credentials
79+
- `BACKSTAGE_API_KEY` - API key authentication
80+
- `BACKSTAGE_SERVICE_ACCOUNT_KEY` - Service account key
7381

7482
### Example Configuration
7583

mcp.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"mcpServers": {
3+
"backstage": {
4+
"command": "node",
5+
"args": ["dist/bundle.cjs"],
6+
"env": {
7+
"BACKSTAGE_BASE_URL": "http://localhost:7007",
8+
"BACKSTAGE_TOKEN": "your-backstage-token"
9+
}
10+
}
11+
}
12+
}

package.json

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"@types/node": "^22.0.0",
1919
"@typescript-eslint/eslint-plugin": "^8.43.0",
2020
"@typescript-eslint/parser": "^8.43.0",
21+
"esbuild": "^0.18.18",
2122
"eslint": "^8.57.0",
2223
"eslint-config-prettier": "^9.0.0",
2324
"eslint-import-resolver-typescript": "^4.4.4",
@@ -26,17 +27,22 @@
2627
"jest": "^29.7.0",
2728
"jest-util": "^29.7.0",
2829
"prettier": "^3.5.3",
30+
"rimraf": "^5.0.0",
2931
"ts-jest": "^29.3.2",
3032
"typescript": "^5.8.3"
3133
},
32-
"main": "dist/index.js",
34+
"main": "dist/bundle.cjs",
3335
"name": "@coderrob/mcp-backstage-server",
3436
"packageManager": "[email protected]",
3537
"scripts": {
36-
"build": "tsc",
38+
"clean": "rimraf dist",
39+
"build:types": "tsc --emitDeclarationOnly",
40+
"build:bundle": "esbuild src/index.ts --bundle --platform=node --target=node22 --format=cjs --outfile=dist/bundle.cjs --minify --sourcemap --metafile=dist/esbuild-meta.json",
41+
"build": "yarn clean && yarn build:bundle",
42+
"build:full": "yarn clean && yarn build:types && tsc && yarn build:bundle",
3743
"lint": "eslint 'src/**/*.ts' --ext .ts",
3844
"lint:fix": "prettier . --write && eslint 'src/**/*.ts' --ext .ts --fix",
39-
"start": "node dist/index.js",
45+
"start": "node dist/bundle.cjs",
4046
"test": "jest --coverage"
4147
},
4248
"types": "dist/index.d.ts",

src/api/backstage-catalog-api.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,18 @@ import {
1818
import { CompoundEntityRef, Entity, stringifyEntityRef } from '@backstage/catalog-model';
1919
import axios, { AxiosInstance, isAxiosError } from 'axios';
2020

21-
import { AuthConfig, AuthManager, securityAuditor } from '../auth';
22-
import { CacheManager } from '../cache';
23-
import { IBackstageCatalogApi, JsonApiDocument, PaginationParams, SecurityEventType } from '../types';
24-
import { isNonEmptyString, isNumber, isString, JsonApiFormatter, logger, PaginationHelper } from '../utils';
21+
import { AuthConfig, AuthManager, securityAuditor } from '../auth/index.js';
22+
import { CacheManager } from '../cache/index.js';
23+
import { IBackstageCatalogApi, JsonApiDocument, PaginationParams, SecurityEventType } from '../types/index.js';
24+
import {
25+
EntityRef,
26+
isNonEmptyString,
27+
isNumber,
28+
isString,
29+
JsonApiFormatter,
30+
logger,
31+
PaginationHelper,
32+
} from '../utils/index.js';
2533

2634
interface BackstageCatalogApiOptions {
2735
baseUrl: string;
@@ -32,18 +40,14 @@ export class BackstageCatalogApi implements IBackstageCatalogApi {
3240
private readonly client: AxiosInstance;
3341
private readonly authManager: AuthManager;
3442
private readonly cacheManager: CacheManager;
35-
private readonly paginationHelper: PaginationHelper;
36-
private readonly jsonApiFormatter: JsonApiFormatter;
3743

3844
constructor({ baseUrl, auth }: BackstageCatalogApiOptions) {
3945
logger.debug('Initializing BackstageCatalogApi', { baseUrl, authType: auth.type });
4046
this.authManager = new AuthManager(auth);
4147
this.cacheManager = new CacheManager();
42-
this.paginationHelper = new PaginationHelper();
43-
this.jsonApiFormatter = new JsonApiFormatter();
4448

4549
this.client = axios.create({
46-
baseURL: `${baseUrl.replace(/\/$/, '')}/v1`,
50+
baseURL: `${baseUrl.replace(/\/$/, '')}/api/catalog`,
4751
timeout: 30000, // 30 second timeout
4852
});
4953
logger.debug('Axios client created with base URL', { baseUrl: this.client.defaults.baseURL });
@@ -204,7 +208,7 @@ export class BackstageCatalogApi implements IBackstageCatalogApi {
204208
): Promise<GetEntityAncestorsResponse> {
205209
const { entityRef } = request;
206210
const { data } = await this.client.get<GetEntityAncestorsResponse>(
207-
`/entities/by-ref/${encodeURIComponent(entityRef)}/ancestry`
211+
`/entities/by-name/${encodeURIComponent(entityRef)}/ancestry`
208212
);
209213
return data;
210214
}
@@ -227,7 +231,13 @@ export class BackstageCatalogApi implements IBackstageCatalogApi {
227231

228232
try {
229233
logger.debug('Fetching entity from API', { entityRef: refString });
230-
const { data } = await this.client.get<Entity>(`/entities/by-ref/${encodeURIComponent(refString)}`);
234+
235+
// Parse the entity reference using the EntityRef class
236+
const entityRef = EntityRef.parse(refString);
237+
238+
const { data } = await this.client.get<Entity>(
239+
`/entities/by-name/${encodeURIComponent(entityRef.kind)}/${encodeURIComponent(entityRef.namespace)}/${encodeURIComponent(entityRef.name)}`
240+
);
231241

232242
// Cache the result for 5 minutes
233243
this.cacheManager.set(cacheKey, data, 5 * 60 * 1000);

src/api/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { BackstageCatalogApi } from './backstage-catalog-api';
1+
export { BackstageCatalogApi } from './backstage-catalog-api.js';

src/auth/auth-manager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import axios, { AxiosResponse } from 'axios';
22

3-
import { isNonEmptyString, isNumber, logger } from '../utils';
3+
import { isNonEmptyString, isNumber, logger } from '../utils/index.js';
44

55
export interface AuthConfig {
66
type: 'bearer' | 'oauth' | 'api-key' | 'service-account';

src/auth/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
/* eslint-disable import/no-unused-modules */
2-
export { type AuthConfig, AuthManager, type TokenInfo } from './auth-manager';
3-
export { InputSanitizer, inputSanitizer } from './input-sanitizer';
4-
export { SecurityAuditor, securityAuditor } from './security-auditor';
2+
export { type AuthConfig, AuthManager, type TokenInfo } from './auth-manager.js';
3+
export { InputSanitizer, inputSanitizer } from './input-sanitizer.js';
4+
export { SecurityAuditor, securityAuditor } from './security-auditor.js';

src/auth/input-sanitizer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { z } from 'zod';
22

3-
import { isObject, isString } from '../utils';
3+
import { isObject, isString } from '../utils/index.js';
44

55
export class InputSanitizer {
66
private readonly maxStringLength = 10000;

src/auth/security-auditor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { z } from 'zod';
22

3-
import { ISecurityEvent, SecurityEventType } from '../types';
3+
import { ISecurityEvent, SecurityEventType } from '../types/index.js';
44

55
const SecurityEventSchema = z.object({
66
id: z.string(),

0 commit comments

Comments
 (0)