@totemsdk/mcp-server 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,18 @@
1
+ import type { SdkIndex } from './types.js';
2
+ export type IndexState = {
3
+ readonly ok: true;
4
+ readonly index: SdkIndex;
5
+ } | {
6
+ readonly ok: false;
7
+ readonly error: string;
8
+ };
9
+ /**
10
+ * Lazily-built, process-wide SDK index state. Never throws: an unavailable
11
+ * index is cached as an error so every call site can return an honest failure
12
+ * instead of an empty (confidently-wrong) index.
13
+ */
14
+ export declare function getIndexState(): IndexState;
15
+ /** The SDK index, or throws {@link IndexUnavailableError} when unavailable. */
16
+ export declare function getIndex(): SdkIndex;
17
+ /** Rebuild the SDK index from the filesystem (used by the `refresh-index` tool). */
18
+ export declare function refreshIndex(): SdkIndex;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getIndexState = getIndexState;
4
+ exports.getIndex = getIndex;
5
+ exports.refreshIndex = refreshIndex;
6
+ const indexer_js_1 = require("./indexer.js");
7
+ let _state = null;
8
+ /**
9
+ * Lazily-built, process-wide SDK index state. Never throws: an unavailable
10
+ * index is cached as an error so every call site can return an honest failure
11
+ * instead of an empty (confidently-wrong) index.
12
+ */
13
+ function getIndexState() {
14
+ if (_state)
15
+ return _state;
16
+ try {
17
+ _state = { ok: true, index: (0, indexer_js_1.buildIndex)() };
18
+ }
19
+ catch (e) {
20
+ _state = {
21
+ ok: false,
22
+ error: e instanceof Error ? e.message : String(e),
23
+ };
24
+ }
25
+ return _state;
26
+ }
27
+ /** The SDK index, or throws {@link IndexUnavailableError} when unavailable. */
28
+ function getIndex() {
29
+ const state = getIndexState();
30
+ if (!state.ok)
31
+ throw new indexer_js_1.IndexUnavailableError(state.error);
32
+ return state.index;
33
+ }
34
+ /** Rebuild the SDK index from the filesystem (used by the `refresh-index` tool). */
35
+ function refreshIndex() {
36
+ _state = null;
37
+ return getIndex();
38
+ }
package/dist/index.d.ts CHANGED
@@ -1 +1,15 @@
1
- export {};
1
+ #!/usr/bin/env node
2
+ export declare const SERVER_INFO: {
3
+ readonly name: "@totemsdk/mcp-server";
4
+ readonly version: string;
5
+ readonly capabilities: {
6
+ readonly resources: {};
7
+ readonly tools: {};
8
+ readonly prompts: {};
9
+ };
10
+ };
11
+ /**
12
+ * @deprecated Importing this at module load forces index construction. Prefer
13
+ * `getIndexState()`/`getIndex()` so a missing index is handled, not thrown.
14
+ */
15
+ export declare const sdkIndex: import("./types.js").SdkIndex;
package/dist/index.js CHANGED
@@ -1,121 +1,106 @@
1
+ #!/usr/bin/env node
1
2
  "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
2
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.sdkIndex = exports.SERVER_INFO = void 0;
3
38
  const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
4
39
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
5
40
  const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
6
- const indexer_js_1 = require("./indexer.js");
41
+ const index_store_js_1 = require("./index-store.js");
7
42
  const resources_js_1 = require("./resources.js");
8
43
  const tools_js_1 = require("./tools.js");
9
- const index = (0, indexer_js_1.buildIndex)();
10
- const server = new index_js_1.Server({ name: '@totemsdk/mcp-server', version: '0.1.0' }, { capabilities: { resources: {}, tools: {}, prompts: {} } });
11
- server.setRequestHandler(types_js_1.ListResourcesRequestSchema, async () => ({
12
- resources: (0, resources_js_1.listResources)(index),
13
- }));
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ function readPkgVersion() {
47
+ try {
48
+ return JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8')).version || '0.0.0';
49
+ }
50
+ catch {
51
+ return '0.0.0';
52
+ }
53
+ }
54
+ const server = new index_js_1.Server({ name: '@totemsdk/mcp-server', version: readPkgVersion() }, { capabilities: { resources: {}, tools: {}, prompts: {} } });
55
+ server.setRequestHandler(types_js_1.ListResourcesRequestSchema, async () => {
56
+ const state = (0, index_store_js_1.getIndexState)();
57
+ // Still advertise resources when the index is unavailable so clients see the
58
+ // server is alive; reads then fail with an actionable message.
59
+ return { resources: (0, resources_js_1.listResources)(state.ok ? state.index : {}) };
60
+ });
14
61
  server.setRequestHandler(types_js_1.ReadResourceRequestSchema, async (request) => {
15
62
  const { uri } = request.params;
16
- const text = (0, resources_js_1.handleResourceRead)(uri, index);
63
+ const state = (0, index_store_js_1.getIndexState)();
64
+ if (!state.ok) {
65
+ throw new Error(`Resource unavailable: ${state.error}`);
66
+ }
67
+ const text = (0, resources_js_1.handleResourceRead)(uri, state.index);
17
68
  if (text === null) {
18
69
  throw new Error(`Resource not found: ${uri}`);
19
70
  }
20
- return { contents: [{ uri, mimeType: 'text/plain', text }] };
71
+ return { contents: [{ uri, mimeType: (0, resources_js_1.resourceMimeType)(uri), text }] };
21
72
  });
22
73
  server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
23
- tools: [
24
- {
25
- name: 'search-symbol',
26
- description: 'Search for a symbol (function, type, class) across all packages',
27
- inputSchema: {
28
- type: 'object',
29
- properties: { query: { type: 'string', description: 'Partial symbol name to search' } },
30
- required: ['query'],
31
- },
32
- },
33
- {
34
- name: 'find-type',
35
- description: 'Find type definitions (interfaces, classes, type aliases) matching a pattern',
36
- inputSchema: {
37
- type: 'object',
38
- properties: { pattern: { type: 'string', description: 'Type name pattern to search' } },
39
- required: ['pattern'],
40
- },
41
- },
42
- {
43
- name: 'dependency-graph',
44
- description: 'Get dependency graph for a package — inbound dependents or outbound dependencies',
45
- inputSchema: {
46
- type: 'object',
47
- properties: {
48
- package: { type: 'string', description: 'Package name (e.g. @totemsdk/edge-opcua)' },
49
- direction: { type: 'string', enum: ['in', 'out', 'all'], description: 'Dependency direction' },
50
- },
51
- required: ['package'],
52
- },
53
- },
54
- {
55
- name: 'validate-import',
56
- description: 'Check whether a cross-package import is valid',
57
- inputSchema: {
58
- type: 'object',
59
- properties: {
60
- from: { type: 'string', description: 'Source package name' },
61
- to: { type: 'string', description: 'Target package name' },
62
- symbol: { type: 'string', description: 'Optional: specific symbol to check' },
63
- },
64
- required: ['from', 'to'],
65
- },
66
- },
67
- {
68
- name: 'scaffold-adapter',
69
- description: 'Generate boilerplate for a new edge protocol adapter',
70
- inputSchema: {
71
- type: 'object',
72
- properties: {
73
- name: { type: 'string', description: 'Package name suffix' },
74
- protocol: { type: 'string', description: 'Protocol name (PascalCase)' },
75
- commands: { type: 'array', items: { type: 'string' }, description: 'Transport port methods' },
76
- },
77
- required: ['name', 'protocol'],
78
- },
79
- },
80
- {
81
- name: 'scaffold-package',
82
- description: 'Generate boilerplate for a new @totemsdk package',
83
- inputSchema: {
84
- type: 'object',
85
- properties: {
86
- name: { type: 'string', description: 'Package name (without @totemsdk/ prefix)' },
87
- deps: { type: 'array', items: { type: 'string' }, description: 'Dependency package names' },
88
- },
89
- required: ['name'],
90
- },
91
- },
92
- {
93
- name: 'package-stats',
94
- description: 'Get statistics about a package — export counts, Rust/Go, tests, deps',
95
- inputSchema: {
96
- type: 'object',
97
- properties: { name: { type: 'string', description: 'Package name' } },
98
- required: ['name'],
99
- },
100
- },
101
- {
102
- name: 'list-exports',
103
- description: 'List exports of a package',
104
- inputSchema: {
105
- type: 'object',
106
- properties: {
107
- package: { type: 'string', description: 'Package name' },
108
- kind: { type: 'string', enum: ['function', 'type', 'interface', 'class', 'const', ''], description: 'Filter by export kind' },
109
- filter: { type: 'string', description: 'Filter by name substring' },
110
- },
111
- required: ['package'],
112
- },
113
- },
114
- ],
74
+ tools: tools_js_1.TOOL_DEFINITIONS,
115
75
  }));
76
+ /**
77
+ * Tools that read the SDK index. The rest (scaffolding, template suggestions)
78
+ * work without it, so a missing index degrades honestly instead of failing
79
+ * everything.
80
+ */
81
+ const INDEX_REQUIRED_TOOLS = new Set([
82
+ 'search-symbol',
83
+ 'find-type',
84
+ 'dependency-graph',
85
+ 'validate-import',
86
+ 'package-stats',
87
+ 'list-exports',
88
+ 'read-source',
89
+ 'list-packages',
90
+ 'search-packages',
91
+ 'refresh-index',
92
+ ]);
116
93
  server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
117
94
  const { name, arguments: args } = request.params;
118
- return (0, tools_js_1.handleToolCall)(name, args || {}, index);
95
+ const state = (0, index_store_js_1.getIndexState)();
96
+ if (!state.ok && INDEX_REQUIRED_TOOLS.has(name)) {
97
+ return {
98
+ content: [{ type: 'text', text: `SDK index unavailable: ${state.error}` }],
99
+ isError: true,
100
+ };
101
+ }
102
+ // Index-free tools ignore the index argument; pass the real one when present.
103
+ return (0, tools_js_1.handleToolCall)(name, args || {}, state.ok ? state.index : {});
119
104
  });
120
105
  server.setRequestHandler(types_js_1.ListPromptsRequestSchema, async () => ({
121
106
  prompts: [
@@ -138,6 +123,11 @@ server.setRequestHandler(types_js_1.ListPromptsRequestSchema, async () => ({
138
123
  }));
139
124
  server.setRequestHandler(types_js_1.GetPromptRequestSchema, async (request) => {
140
125
  const { name, arguments: args } = request.params;
126
+ const indexState = (0, index_store_js_1.getIndexState)();
127
+ if (!indexState.ok) {
128
+ throw new Error(`SDK index unavailable: ${indexState.error}`);
129
+ }
130
+ const index = indexState.index;
141
131
  if (name === 'analyze-cross-package') {
142
132
  const from = args?.from;
143
133
  const to = args?.to;
@@ -209,6 +199,19 @@ server.setRequestHandler(types_js_1.GetPromptRequestSchema, async (request) => {
209
199
  }
210
200
  throw new Error(`Prompt not found: ${name}`);
211
201
  });
202
+ exports.SERVER_INFO = {
203
+ name: '@totemsdk/mcp-server',
204
+ version: readPkgVersion(),
205
+ capabilities: { resources: {}, tools: {}, prompts: {} },
206
+ };
207
+ /**
208
+ * @deprecated Importing this at module load forces index construction. Prefer
209
+ * `getIndexState()`/`getIndex()` so a missing index is handled, not thrown.
210
+ */
211
+ exports.sdkIndex = (() => {
212
+ const state = (0, index_store_js_1.getIndexState)();
213
+ return state.ok ? state.index : { generatedAt: 0, packages: {}, symbolIndex: {}, domainMap: {} };
214
+ })();
212
215
  async function main() {
213
216
  const transport = new stdio_js_1.StdioServerTransport();
214
217
  await server.connect(transport);
package/dist/indexer.d.ts CHANGED
@@ -1,3 +1,29 @@
1
1
  import type { SdkIndex } from './types.js';
2
- export declare function buildIndex(): SdkIndex;
2
+ /**
3
+ * Thrown when no SDK index can be produced — neither a monorepo to scan nor a
4
+ * bundled index shipped in the package. Callers must surface this as an error,
5
+ * never as an empty (and therefore confidently-wrong) index.
6
+ */
7
+ export declare class IndexUnavailableError extends Error {
8
+ constructor(message: string);
9
+ }
10
+ export interface BuildIndexOptions {
11
+ /**
12
+ * Monorepo root to scan. Use `null` to force the non-monorepo path (bundled
13
+ * index or an error). Defaults to auto-detection.
14
+ */
15
+ rootDir?: string | null;
16
+ /** Never fall back to the bundled index (used by tests / strict callers). */
17
+ noBundled?: boolean;
18
+ }
19
+ /** Scan a monorepo root into an `SdkIndex`. */
20
+ export declare function buildFromMonorepo(root: string, now?: number): SdkIndex;
21
+ /**
22
+ * Build the SDK index. Prefers a live monorepo scan; falls back to the bundled
23
+ * index shipped in the package. Throws {@link IndexUnavailableError} when
24
+ * neither is available — an empty index is never returned, because downstream
25
+ * tools would otherwise answer "not found" as if it were fact.
26
+ */
27
+ export declare function buildIndex(options?: BuildIndexOptions): SdkIndex;
28
+ /** Read a file from a package's `src/` directory (for the `read-source` tool). */
3
29
  export declare function readSourceFile(pkgName: string, filePath: string): string | null;