@cityjson/cj-mcp 0.1.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.
- package/dist/index.js +202 -0
- package/package.json +37 -0
- package/specs/chapters/appearance-object.md +186 -0
- package/specs/chapters/citygml-v30-implementation-details.md +7 -0
- package/specs/chapters/cityjson-object.md +62 -0
- package/specs/chapters/cityjson-schemas.md +3 -0
- package/specs/chapters/extensions.md +296 -0
- package/specs/chapters/geometry-objects.md +362 -0
- package/specs/chapters/handling-large-files.md +93 -0
- package/specs/chapters/metadata.md +118 -0
- package/specs/chapters/profile-and-date.md +22 -0
- package/specs/chapters/the-different-city-objects.md +571 -0
- package/specs/chapters/transform-object.md +22 -0
- package/specs/index.json +128 -0
- package/src/config.ts +47 -0
- package/src/constants.ts +7 -0
- package/src/index.ts +59 -0
- package/src/schemas/tool-schemas.ts +26 -0
- package/src/server.ts +38 -0
- package/src/services/chapter-index.ts +37 -0
- package/src/services/spec-loader.ts +31 -0
- package/src/tools/index.ts +6 -0
- package/src/tools/read-chapter.ts +74 -0
- package/src/tools/read-outline.ts +44 -0
- package/src/types.ts +23 -0
- package/tsconfig.json +8 -0
- package/vite.config.ts +47 -0
package/specs/index.json
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "2.0.1",
|
|
3
|
+
"source": "https://www.cityjson.org/specs/2.0.1/",
|
|
4
|
+
"generated_at": "2025-12-20T12:19:52.522Z",
|
|
5
|
+
"chapters": [
|
|
6
|
+
{
|
|
7
|
+
"id": "profile-and-date",
|
|
8
|
+
"title": "Living Standard,\n 20 December 2025",
|
|
9
|
+
"path": "chapters/profile-and-date.md",
|
|
10
|
+
"order": 1,
|
|
11
|
+
"sections": []
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"id": "cityjson-object",
|
|
15
|
+
"title": "1. CityJSON Object",
|
|
16
|
+
"path": "chapters/cityjson-object.md",
|
|
17
|
+
"order": 2,
|
|
18
|
+
"sections": []
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "the-different-city-objects",
|
|
22
|
+
"title": "2. The different City Objects",
|
|
23
|
+
"path": "chapters/the-different-city-objects.md",
|
|
24
|
+
"order": 3,
|
|
25
|
+
"sections": [
|
|
26
|
+
"2-1-attributes-for-all-city-objects",
|
|
27
|
+
"2-2-bridge",
|
|
28
|
+
"2-3-building",
|
|
29
|
+
"2-4-cityfurniture",
|
|
30
|
+
"2-5-cityobjectgroup",
|
|
31
|
+
"2-6-genericcityobject",
|
|
32
|
+
"2-7-landuse",
|
|
33
|
+
"2-8-otherconstruction",
|
|
34
|
+
"2-9-plantcover",
|
|
35
|
+
"2-10-solitaryvegetationobject",
|
|
36
|
+
"2-11-tinrelief",
|
|
37
|
+
"2-12-transportation",
|
|
38
|
+
"2-13-tunnel",
|
|
39
|
+
"2-14-waterbody"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "geometry-objects",
|
|
44
|
+
"title": "3. Geometry Objects",
|
|
45
|
+
"path": "chapters/geometry-objects.md",
|
|
46
|
+
"order": 4,
|
|
47
|
+
"sections": [
|
|
48
|
+
"3-1-coordinates-of-the-vertices",
|
|
49
|
+
"3-2-arrays-to-represent-boundaries",
|
|
50
|
+
"3-3-semantics-of-geometric-primitives",
|
|
51
|
+
"3-4-geometry-templates"
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"id": "transform-object",
|
|
56
|
+
"title": "4. Transform Object",
|
|
57
|
+
"path": "chapters/transform-object.md",
|
|
58
|
+
"order": 5,
|
|
59
|
+
"sections": []
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"id": "metadata",
|
|
63
|
+
"title": "5. Metadata",
|
|
64
|
+
"path": "chapters/metadata.md",
|
|
65
|
+
"order": 6,
|
|
66
|
+
"sections": [
|
|
67
|
+
"5-1-geographicalextent-bbox",
|
|
68
|
+
"5-2-identifier",
|
|
69
|
+
"5-3-pointofcontact",
|
|
70
|
+
"5-4-referencedate",
|
|
71
|
+
"5-5-referencesystem-crs",
|
|
72
|
+
"5-6-title"
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"id": "appearance-object",
|
|
77
|
+
"title": "6. Appearance Object",
|
|
78
|
+
"path": "chapters/appearance-object.md",
|
|
79
|
+
"order": 7,
|
|
80
|
+
"sections": [
|
|
81
|
+
"6-1-geometry-object-having-material-s",
|
|
82
|
+
"6-2-geometry-object-having-texture-s",
|
|
83
|
+
"6-3-material-object",
|
|
84
|
+
"6-4-texture-object",
|
|
85
|
+
"6-5-vertices-texture-object"
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"id": "handling-large-files",
|
|
90
|
+
"title": "7. Handling large files",
|
|
91
|
+
"path": "chapters/handling-large-files.md",
|
|
92
|
+
"order": 8,
|
|
93
|
+
"sections": [
|
|
94
|
+
"7-1-decomposing-an-area-into-parts-tiles",
|
|
95
|
+
"7-2-text-sequences-and-streaming-with-cityjsonfeature"
|
|
96
|
+
]
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"id": "extensions",
|
|
100
|
+
"title": "8. Extensions",
|
|
101
|
+
"path": "chapters/extensions.md",
|
|
102
|
+
"order": 9,
|
|
103
|
+
"sections": [
|
|
104
|
+
"8-1-using-an-extension-in-a-cityjson-file",
|
|
105
|
+
"8-2-the-extension-file",
|
|
106
|
+
"8-3-case-1-adding-new-properties-at-the-root-of-a-document",
|
|
107
|
+
"8-4-case-2-defining-attributes-for-existing-city-objects",
|
|
108
|
+
"8-5-case-3-defining-a-new-semantic-object",
|
|
109
|
+
"8-6-case-4-creating-and-or-extending-new-city-objects",
|
|
110
|
+
"8-7-rules-to-follow-to-define-new-city-objects"
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"id": "cityjson-schemas",
|
|
115
|
+
"title": "9. CityJSON schemas",
|
|
116
|
+
"path": "chapters/cityjson-schemas.md",
|
|
117
|
+
"order": 10,
|
|
118
|
+
"sections": []
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"id": "citygml-v30-implementation-details",
|
|
122
|
+
"title": "10. CityGML v3.0 implementation details",
|
|
123
|
+
"path": "chapters/citygml-v30-implementation-details.md",
|
|
124
|
+
"order": 11,
|
|
125
|
+
"sections": []
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration management
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
6
|
+
import { dirname, resolve } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { DEFAULT_HTTP_PORT, SERVER_NAME, SERVER_VERSION } from "./constants.js";
|
|
9
|
+
|
|
10
|
+
export interface ServerConfig {
|
|
11
|
+
name: string;
|
|
12
|
+
version: string;
|
|
13
|
+
specsPath: string;
|
|
14
|
+
transport: "stdio" | "http";
|
|
15
|
+
httpPort: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Find project root by looking for pnpm-workspace.yaml
|
|
20
|
+
*/
|
|
21
|
+
function findProjectRoot(startDir: string): string {
|
|
22
|
+
let current = startDir;
|
|
23
|
+
while (current !== "/" && !existsSync(resolve(current, "pnpm-workspace.yaml"))) {
|
|
24
|
+
current = dirname(current);
|
|
25
|
+
}
|
|
26
|
+
return current;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Load configuration from environment variables
|
|
31
|
+
*/
|
|
32
|
+
export function loadConfig(): ServerConfig {
|
|
33
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
34
|
+
const projectRoot = findProjectRoot(__dirname);
|
|
35
|
+
|
|
36
|
+
const specsPath = process.env.CITYJSON_SPECS_PATH || resolve(projectRoot, "specs");
|
|
37
|
+
const transport = (process.env.TRANSPORT as "stdio" | "http") || "stdio";
|
|
38
|
+
const httpPort = process.env.PORT ? Number.parseInt(process.env.PORT, 10) : DEFAULT_HTTP_PORT;
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
name: SERVER_NAME,
|
|
42
|
+
version: SERVER_VERSION,
|
|
43
|
+
specsPath,
|
|
44
|
+
transport,
|
|
45
|
+
httpPort,
|
|
46
|
+
};
|
|
47
|
+
}
|
package/src/constants.ts
ADDED
package/src/index.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Entry point for MCP server
|
|
4
|
+
* Supports both stdio and HTTP transports
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
|
+
import { loadConfig } from "./config.js";
|
|
9
|
+
import { createServer } from "./server.js";
|
|
10
|
+
|
|
11
|
+
async function main(): Promise<void> {
|
|
12
|
+
const config = loadConfig();
|
|
13
|
+
const server = createServer(config);
|
|
14
|
+
|
|
15
|
+
if (config.transport === "stdio") {
|
|
16
|
+
const transport = new StdioServerTransport();
|
|
17
|
+
await server.connect(transport);
|
|
18
|
+
console.error("CityJSON Spec MCP Server running on stdio");
|
|
19
|
+
console.error(`Specs path: ${config.specsPath}`);
|
|
20
|
+
} else {
|
|
21
|
+
// HTTP transport - dynamically import to avoid loading express in stdio mode
|
|
22
|
+
const express = await import("express");
|
|
23
|
+
const { StreamableHTTPServerTransport } = await import(
|
|
24
|
+
"@modelcontextprotocol/sdk/server/streamableHttp.js"
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const app = express.default();
|
|
28
|
+
app.use(express.json());
|
|
29
|
+
|
|
30
|
+
app.post("/mcp", async (req, res) => {
|
|
31
|
+
const transport = new StreamableHTTPServerTransport({
|
|
32
|
+
sessionIdGenerator: undefined,
|
|
33
|
+
enableJsonResponse: true,
|
|
34
|
+
});
|
|
35
|
+
res.on("close", () => transport.close());
|
|
36
|
+
await server.connect(transport);
|
|
37
|
+
await transport.handleRequest(req, res, req.body);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Health check endpoint for Cloud Run
|
|
41
|
+
app.get("/", (_req, res) => {
|
|
42
|
+
res.status(200).json({ status: "ok", service: "cityjson-spec-mcp" });
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
app.get("/health", (_req, res) => {
|
|
46
|
+
res.status(200).json({ status: "ok" });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
app.listen(config.httpPort, () => {
|
|
50
|
+
console.error(`CityJSON Spec MCP Server running on http://localhost:${config.httpPort}`);
|
|
51
|
+
console.error(`Specs path: ${config.specsPath}`);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
main().catch((error) => {
|
|
57
|
+
console.error("Failed to start server:", error);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schemas for tool inputs
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
7
|
+
export const ReadOutlineInputSchema = z
|
|
8
|
+
.object({
|
|
9
|
+
include_sections: z
|
|
10
|
+
.boolean()
|
|
11
|
+
.default(true)
|
|
12
|
+
.describe("Include section headings within each chapter"),
|
|
13
|
+
})
|
|
14
|
+
.strict();
|
|
15
|
+
|
|
16
|
+
export const ReadChapterInputSchema = z
|
|
17
|
+
.object({
|
|
18
|
+
chapter: z
|
|
19
|
+
.string()
|
|
20
|
+
.min(1)
|
|
21
|
+
.describe("Chapter identifier (e.g., 'metadata', 'city-objects', 'geometry-objects')"),
|
|
22
|
+
})
|
|
23
|
+
.strict();
|
|
24
|
+
|
|
25
|
+
export type ReadOutlineInput = z.infer<typeof ReadOutlineInputSchema>;
|
|
26
|
+
export type ReadChapterInput = z.infer<typeof ReadChapterInputSchema>;
|
package/src/server.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server initialization and tool registration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import type { ServerConfig } from "./config.js";
|
|
7
|
+
import { ReadChapterInputSchema, ReadOutlineInputSchema } from "./schemas/tool-schemas.js";
|
|
8
|
+
import { ChapterIndexService } from "./services/chapter-index.js";
|
|
9
|
+
import { SpecLoader } from "./services/spec-loader.js";
|
|
10
|
+
import { handleReadChapter, handleReadOutline } from "./tools/index.js";
|
|
11
|
+
|
|
12
|
+
export function createServer(config: ServerConfig): McpServer {
|
|
13
|
+
const server = new McpServer(
|
|
14
|
+
{ name: config.name, version: config.version },
|
|
15
|
+
{ capabilities: { tools: {} } },
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const indexService = new ChapterIndexService(config.specsPath);
|
|
19
|
+
const specLoader = new SpecLoader(config.specsPath);
|
|
20
|
+
|
|
21
|
+
// Register cityjson_read_spec_outline
|
|
22
|
+
server.tool(
|
|
23
|
+
"cityjson_read_spec_outline",
|
|
24
|
+
"Returns the table of contents for the CityJSON specification, including all chapters and their sections",
|
|
25
|
+
ReadOutlineInputSchema.shape,
|
|
26
|
+
async (params) => handleReadOutline(params, indexService),
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
// Register cityjson_read_spec_chapter
|
|
30
|
+
server.tool(
|
|
31
|
+
"cityjson_read_spec_chapter",
|
|
32
|
+
"Returns the full specification content for a specific chapter in Markdown format",
|
|
33
|
+
ReadChapterInputSchema.shape,
|
|
34
|
+
async (params) => handleReadChapter(params, specLoader, indexService),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return server;
|
|
38
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chapter index service - manages the index.json file
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import type { Chapter, ChapterIndex } from "../types.js";
|
|
8
|
+
|
|
9
|
+
export class ChapterIndexService {
|
|
10
|
+
private index: ChapterIndex | null = null;
|
|
11
|
+
private specsPath: string;
|
|
12
|
+
|
|
13
|
+
constructor(specsPath: string) {
|
|
14
|
+
this.specsPath = specsPath;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async loadIndex(): Promise<ChapterIndex> {
|
|
18
|
+
if (this.index) {
|
|
19
|
+
return this.index;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const indexPath = join(this.specsPath, "index.json");
|
|
23
|
+
const content = await readFile(indexPath, "utf-8");
|
|
24
|
+
this.index = JSON.parse(content) as ChapterIndex;
|
|
25
|
+
return this.index;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async getChapterById(id: string): Promise<Chapter | undefined> {
|
|
29
|
+
const index = await this.loadIndex();
|
|
30
|
+
return index.chapters.find((c) => c.id === id);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async listChapterIds(): Promise<string[]> {
|
|
34
|
+
const index = await this.loadIndex();
|
|
35
|
+
return index.chapters.map((c) => c.id);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec loader service - loads chapter Markdown files
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
|
|
8
|
+
export class SpecLoader {
|
|
9
|
+
private cache: Map<string, string> = new Map();
|
|
10
|
+
private specsPath: string;
|
|
11
|
+
|
|
12
|
+
constructor(specsPath: string) {
|
|
13
|
+
this.specsPath = specsPath;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async loadChapter(chapterId: string): Promise<string> {
|
|
17
|
+
// Check cache first
|
|
18
|
+
const cached = this.cache.get(chapterId);
|
|
19
|
+
if (cached) {
|
|
20
|
+
return cached;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Read from file
|
|
24
|
+
const chapterPath = join(this.specsPath, "chapters", `${chapterId}.md`);
|
|
25
|
+
const content = await readFile(chapterPath, "utf-8");
|
|
26
|
+
|
|
27
|
+
// Cache and return
|
|
28
|
+
this.cache.set(chapterId, content);
|
|
29
|
+
return content;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Implementation of cityjson_read_spec_chapter tool
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { ReadChapterInput } from "../schemas/tool-schemas.js";
|
|
6
|
+
import type { ChapterIndexService } from "../services/chapter-index.js";
|
|
7
|
+
import type { SpecLoader } from "../services/spec-loader.js";
|
|
8
|
+
import type { ToolResult } from "../types.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Normalize chapter ID for flexible matching:
|
|
12
|
+
* - lowercase
|
|
13
|
+
* - trim whitespace
|
|
14
|
+
* - replace spaces/underscores with hyphens
|
|
15
|
+
* - remove special characters
|
|
16
|
+
*/
|
|
17
|
+
function normalizeChapterId(id: string): string {
|
|
18
|
+
return id
|
|
19
|
+
.toLowerCase()
|
|
20
|
+
.trim()
|
|
21
|
+
.replace(/[\s_]+/g, "-") // spaces/underscores to hyphens
|
|
22
|
+
.replace(/[^a-z0-9-]/g, "") // remove special chars
|
|
23
|
+
.replace(/-+/g, "-") // collapse multiple hyphens
|
|
24
|
+
.replace(/^-|-$/g, ""); // trim leading/trailing hyphens
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function handleReadChapter(
|
|
28
|
+
params: ReadChapterInput,
|
|
29
|
+
specLoader: SpecLoader,
|
|
30
|
+
indexService: ChapterIndexService,
|
|
31
|
+
): Promise<ToolResult> {
|
|
32
|
+
const validChapters = await indexService.listChapterIds();
|
|
33
|
+
const normalizedInput = normalizeChapterId(params.chapter);
|
|
34
|
+
|
|
35
|
+
// Find matching chapter (exact or normalized match)
|
|
36
|
+
const matchedChapter = validChapters.find(
|
|
37
|
+
(chapterId) =>
|
|
38
|
+
chapterId === params.chapter || normalizeChapterId(chapterId) === normalizedInput,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
if (!matchedChapter) {
|
|
42
|
+
return {
|
|
43
|
+
isError: true,
|
|
44
|
+
content: [
|
|
45
|
+
{
|
|
46
|
+
type: "text",
|
|
47
|
+
text: `Chapter '${
|
|
48
|
+
params.chapter
|
|
49
|
+
}' not found. Valid chapters: ${validChapters.join(", ")}`,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
const content = await specLoader.loadChapter(matchedChapter);
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
content: [{ type: "text", text: content }],
|
|
60
|
+
};
|
|
61
|
+
} catch (error) {
|
|
62
|
+
return {
|
|
63
|
+
isError: true,
|
|
64
|
+
content: [
|
|
65
|
+
{
|
|
66
|
+
type: "text",
|
|
67
|
+
text: `Error loading chapter '${matchedChapter}': ${
|
|
68
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
69
|
+
}`,
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Implementation of cityjson_read_spec_outline tool
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { ReadOutlineInput } from "../schemas/tool-schemas.js";
|
|
6
|
+
import type { ChapterIndexService } from "../services/chapter-index.js";
|
|
7
|
+
import type { ToolResult } from "../types.js";
|
|
8
|
+
|
|
9
|
+
export async function handleReadOutline(
|
|
10
|
+
params: ReadOutlineInput,
|
|
11
|
+
indexService: ChapterIndexService,
|
|
12
|
+
): Promise<ToolResult> {
|
|
13
|
+
try {
|
|
14
|
+
const index = await indexService.loadIndex();
|
|
15
|
+
|
|
16
|
+
const result = {
|
|
17
|
+
version: index.version,
|
|
18
|
+
total_chapters: index.chapters.length,
|
|
19
|
+
chapters: params.include_sections
|
|
20
|
+
? index.chapters
|
|
21
|
+
: index.chapters.map((c) => ({
|
|
22
|
+
id: c.id,
|
|
23
|
+
title: c.title,
|
|
24
|
+
order: c.order,
|
|
25
|
+
})),
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
30
|
+
};
|
|
31
|
+
} catch (error) {
|
|
32
|
+
return {
|
|
33
|
+
isError: true,
|
|
34
|
+
content: [
|
|
35
|
+
{
|
|
36
|
+
type: "text",
|
|
37
|
+
text: `Error loading specification index: ${
|
|
38
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
39
|
+
}`,
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for mcp-server
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface Chapter {
|
|
6
|
+
id: string;
|
|
7
|
+
title: string;
|
|
8
|
+
path: string;
|
|
9
|
+
order: number;
|
|
10
|
+
sections: string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ChapterIndex {
|
|
14
|
+
version: string;
|
|
15
|
+
source: string;
|
|
16
|
+
generated_at: string;
|
|
17
|
+
chapters: Chapter[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ToolResult {
|
|
21
|
+
content: Array<{ type: "text"; text: string }>;
|
|
22
|
+
isError?: boolean;
|
|
23
|
+
}
|
package/tsconfig.json
ADDED
package/vite.config.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { defineConfig } from "vite";
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
build: {
|
|
6
|
+
lib: {
|
|
7
|
+
entry: resolve(__dirname, "src/index.ts"),
|
|
8
|
+
formats: ["es"],
|
|
9
|
+
fileName: "index",
|
|
10
|
+
},
|
|
11
|
+
rollupOptions: {
|
|
12
|
+
external: [
|
|
13
|
+
// Node.js built-ins
|
|
14
|
+
/^node:/,
|
|
15
|
+
"http",
|
|
16
|
+
"http2",
|
|
17
|
+
"https",
|
|
18
|
+
"stream",
|
|
19
|
+
"crypto",
|
|
20
|
+
"fs",
|
|
21
|
+
"path",
|
|
22
|
+
"url",
|
|
23
|
+
"util",
|
|
24
|
+
"buffer",
|
|
25
|
+
"events",
|
|
26
|
+
"net",
|
|
27
|
+
"tls",
|
|
28
|
+
"os",
|
|
29
|
+
"zlib",
|
|
30
|
+
"child_process",
|
|
31
|
+
// External packages - externalize all dependencies
|
|
32
|
+
"@modelcontextprotocol/sdk",
|
|
33
|
+
/^@modelcontextprotocol\/sdk\/.*/,
|
|
34
|
+
"express",
|
|
35
|
+
"zod",
|
|
36
|
+
/^@hono\/.*/,
|
|
37
|
+
"hono",
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
target: "node20",
|
|
41
|
+
outDir: "dist",
|
|
42
|
+
// Don't minify for Node.js
|
|
43
|
+
minify: false,
|
|
44
|
+
// Disable SSR-related optimizations
|
|
45
|
+
ssr: true,
|
|
46
|
+
},
|
|
47
|
+
});
|