swiftipedia-mcp-server 1.0.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/README.md +36 -0
- package/dist/api.d.ts +44 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +223 -0
- package/dist/api.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +176 -0
- package/dist/index.js.map +1 -0
- package/package.json +30 -0
- package/src/api.ts +310 -0
- package/src/index.ts +206 -0
- package/tsconfig.json +24 -0
package/README.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Swiftipedia MCP Server
|
|
2
|
+
|
|
3
|
+
Model Context Protocol (MCP) server for accessing [Swiftipedia](https://swiftipedia.org) documentation.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx -y swiftipedia-mcp-server
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage with Cursor
|
|
12
|
+
|
|
13
|
+
Add to your `~/.cursor/mcp.json`:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"mcpServers": {
|
|
18
|
+
"swiftipedia": {
|
|
19
|
+
"command": "npx",
|
|
20
|
+
"args": ["-y", "swiftipedia-mcp-server"]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Tools
|
|
27
|
+
|
|
28
|
+
- `search_symbols` - Search for SwiftUI/Swift API symbols
|
|
29
|
+
- `get_symbol_details` - Get complete documentation for a symbol
|
|
30
|
+
- `get_examples` - Get code examples for a symbol
|
|
31
|
+
- `browse_module` - List all symbols in a module
|
|
32
|
+
|
|
33
|
+
## License
|
|
34
|
+
|
|
35
|
+
MIT
|
|
36
|
+
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
interface SearchResult {
|
|
2
|
+
name: string;
|
|
3
|
+
type: string;
|
|
4
|
+
module: string;
|
|
5
|
+
description: string;
|
|
6
|
+
url: string;
|
|
7
|
+
availability?: string[];
|
|
8
|
+
}
|
|
9
|
+
interface CodeExample {
|
|
10
|
+
code: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
}
|
|
13
|
+
interface SymbolDetails {
|
|
14
|
+
name: string;
|
|
15
|
+
type: string;
|
|
16
|
+
module: string;
|
|
17
|
+
description: string;
|
|
18
|
+
declaration: string;
|
|
19
|
+
availability: string[];
|
|
20
|
+
examples: CodeExample[];
|
|
21
|
+
relatedSymbols: {
|
|
22
|
+
name: string;
|
|
23
|
+
url: string;
|
|
24
|
+
}[];
|
|
25
|
+
conformsTo?: string[];
|
|
26
|
+
url: string;
|
|
27
|
+
}
|
|
28
|
+
export declare class SwiftipediaAPI {
|
|
29
|
+
private symbolsCache;
|
|
30
|
+
private detailsCache;
|
|
31
|
+
private cdnBase;
|
|
32
|
+
constructor(cdnBase: string);
|
|
33
|
+
private loadSymbols;
|
|
34
|
+
private fetchDocumentation;
|
|
35
|
+
private extractText;
|
|
36
|
+
private extractExamples;
|
|
37
|
+
private calculateRelevance;
|
|
38
|
+
searchSymbols(query: string, moduleFilter?: string, limit?: number): Promise<SearchResult[]>;
|
|
39
|
+
getSymbolDetails(path: string): Promise<SymbolDetails>;
|
|
40
|
+
getExamples(path: string): Promise<CodeExample[]>;
|
|
41
|
+
browseModule(module: string, categoryFilter?: string, limit?: number): Promise<SearchResult[]>;
|
|
42
|
+
}
|
|
43
|
+
export {};
|
|
44
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAKA,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,cAAc,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAChD,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,YAAY,CAAyB;IAC7C,OAAO,CAAC,YAAY,CAA0B;IAC9C,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,MAAM;YAIb,WAAW;YAiBX,kBAAkB;IAkBhC,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,eAAe;IAmBvB,OAAO,CAAC,kBAAkB;IAiBpB,aAAa,CACjB,KAAK,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,MAAM,EACrB,KAAK,GAAE,MAAW,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC;IAmDpB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAwEtD,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAUjD,YAAY,CAChB,MAAM,EAAE,MAAM,EACd,cAAc,CAAC,EAAE,MAAM,EACvB,KAAK,GAAE,MAAW,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC;CAuC3B"}
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
export class SwiftipediaAPI {
|
|
2
|
+
constructor(cdnBase) {
|
|
3
|
+
this.symbolsCache = null;
|
|
4
|
+
this.detailsCache = new Map();
|
|
5
|
+
this.cdnBase = cdnBase;
|
|
6
|
+
}
|
|
7
|
+
async loadSymbols() {
|
|
8
|
+
if (this.symbolsCache) {
|
|
9
|
+
return this.symbolsCache;
|
|
10
|
+
}
|
|
11
|
+
const url = `${this.cdnBase}/symbols.json`;
|
|
12
|
+
const response = await fetch(url);
|
|
13
|
+
if (!response.ok) {
|
|
14
|
+
throw new Error(`Failed to fetch symbols: ${response.statusText}`);
|
|
15
|
+
}
|
|
16
|
+
const data = await response.json();
|
|
17
|
+
this.symbolsCache = data;
|
|
18
|
+
return this.symbolsCache;
|
|
19
|
+
}
|
|
20
|
+
async fetchDocumentation(path) {
|
|
21
|
+
if (this.detailsCache.has(path)) {
|
|
22
|
+
return this.detailsCache.get(path);
|
|
23
|
+
}
|
|
24
|
+
let cleanPath = path.replace(/^\/documentation\//, "");
|
|
25
|
+
const url = `${this.cdnBase}/documentation/data/documentation/${cleanPath}.json`;
|
|
26
|
+
const response = await fetch(url);
|
|
27
|
+
if (!response.ok) {
|
|
28
|
+
throw new Error(`Symbol not found: ${path}`);
|
|
29
|
+
}
|
|
30
|
+
const data = await response.json();
|
|
31
|
+
this.detailsCache.set(path, data);
|
|
32
|
+
return data;
|
|
33
|
+
}
|
|
34
|
+
extractText(content) {
|
|
35
|
+
if (!content)
|
|
36
|
+
return "";
|
|
37
|
+
return content
|
|
38
|
+
.map((item) => {
|
|
39
|
+
if (item.type === "text")
|
|
40
|
+
return item.text;
|
|
41
|
+
if (item.type === "codeVoice")
|
|
42
|
+
return `\`${item.code}\``;
|
|
43
|
+
if (item.inlineContent)
|
|
44
|
+
return this.extractText(item.inlineContent);
|
|
45
|
+
return "";
|
|
46
|
+
})
|
|
47
|
+
.join("");
|
|
48
|
+
}
|
|
49
|
+
extractExamples(sections) {
|
|
50
|
+
const examples = [];
|
|
51
|
+
for (const section of sections) {
|
|
52
|
+
if (section.kind === "content" && section.content) {
|
|
53
|
+
for (const item of section.content) {
|
|
54
|
+
if (item.type === "codeListing" && item.syntax === "swift") {
|
|
55
|
+
examples.push({
|
|
56
|
+
code: item.code.join("\n"),
|
|
57
|
+
description: undefined,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return examples;
|
|
64
|
+
}
|
|
65
|
+
calculateRelevance(symbol, query) {
|
|
66
|
+
const lowerQuery = query.toLowerCase();
|
|
67
|
+
const lowerTitle = symbol.title.toLowerCase();
|
|
68
|
+
if (lowerTitle === lowerQuery)
|
|
69
|
+
return 100;
|
|
70
|
+
if (lowerTitle.startsWith(lowerQuery))
|
|
71
|
+
return 90;
|
|
72
|
+
if (lowerTitle.includes(lowerQuery))
|
|
73
|
+
return 70;
|
|
74
|
+
const words = lowerQuery.split(/\s+/);
|
|
75
|
+
const matchedWords = words.filter((word) => lowerTitle.includes(word)).length;
|
|
76
|
+
if (matchedWords > 0)
|
|
77
|
+
return 50 + (matchedWords / words.length) * 20;
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
80
|
+
async searchSymbols(query, moduleFilter, limit = 10) {
|
|
81
|
+
const symbols = await this.loadSymbols();
|
|
82
|
+
let filtered = symbols.filter((symbol) => {
|
|
83
|
+
if (moduleFilter &&
|
|
84
|
+
!symbol.url.includes(`/documentation/${moduleFilter}/`)) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
return this.calculateRelevance(symbol, query) > 0;
|
|
88
|
+
});
|
|
89
|
+
filtered.sort((a, b) => {
|
|
90
|
+
return this.calculateRelevance(b, query) - this.calculateRelevance(a, query);
|
|
91
|
+
});
|
|
92
|
+
filtered = filtered.slice(0, limit);
|
|
93
|
+
const results = [];
|
|
94
|
+
for (const symbol of filtered) {
|
|
95
|
+
const module = symbol.url.split("/")[2] || "unknown";
|
|
96
|
+
try {
|
|
97
|
+
const doc = await this.fetchDocumentation(symbol.url);
|
|
98
|
+
const description = doc.abstract ? this.extractText(doc.abstract) : "";
|
|
99
|
+
const type = doc.metadata?.symbolKind || "symbol";
|
|
100
|
+
const platforms = doc.metadata?.platforms?.map((p) => p.name) || [];
|
|
101
|
+
results.push({
|
|
102
|
+
name: symbol.title,
|
|
103
|
+
type,
|
|
104
|
+
module,
|
|
105
|
+
description,
|
|
106
|
+
url: `https://swiftipedia.org${symbol.url}`,
|
|
107
|
+
availability: platforms,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
results.push({
|
|
112
|
+
name: symbol.title,
|
|
113
|
+
type: "unknown",
|
|
114
|
+
module,
|
|
115
|
+
description: "",
|
|
116
|
+
url: `https://swiftipedia.org${symbol.url}`,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return results;
|
|
121
|
+
}
|
|
122
|
+
async getSymbolDetails(path) {
|
|
123
|
+
const doc = await this.fetchDocumentation(path);
|
|
124
|
+
const name = doc.metadata?.title || "Unknown";
|
|
125
|
+
const type = doc.metadata?.symbolKind || "symbol";
|
|
126
|
+
const module = path.split("/")[2] || "unknown";
|
|
127
|
+
const description = doc.abstract ? this.extractText(doc.abstract) : "";
|
|
128
|
+
let declaration = "";
|
|
129
|
+
if (doc.primaryContentSections) {
|
|
130
|
+
for (const section of doc.primaryContentSections) {
|
|
131
|
+
if (section.kind === "declarations" && section.declarations?.[0]?.tokens) {
|
|
132
|
+
declaration = section.declarations[0].tokens.map((t) => t.text).join("");
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const examples = doc.primaryContentSections
|
|
138
|
+
? this.extractExamples(doc.primaryContentSections)
|
|
139
|
+
: [];
|
|
140
|
+
const platforms = doc.metadata?.platforms?.map((p) => `${p.name} ${p.introducedAt}${p.deprecated ? " (deprecated)" : ""}`) || [];
|
|
141
|
+
const relatedSymbols = [];
|
|
142
|
+
if (doc.topicSections) {
|
|
143
|
+
for (const section of doc.topicSections) {
|
|
144
|
+
if (section.identifiers) {
|
|
145
|
+
for (const id of section.identifiers.slice(0, 5)) {
|
|
146
|
+
if (doc.references?.[id]) {
|
|
147
|
+
const ref = doc.references[id];
|
|
148
|
+
relatedSymbols.push({
|
|
149
|
+
name: ref.title || ref.name || id,
|
|
150
|
+
url: `https://swiftipedia.org${ref.url || ""}`,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const conformsTo = [];
|
|
158
|
+
if (doc.relationshipsSections) {
|
|
159
|
+
for (const section of doc.relationshipsSections) {
|
|
160
|
+
if (section.type === "conformsTo" && section.identifiers) {
|
|
161
|
+
for (const id of section.identifiers) {
|
|
162
|
+
if (doc.references?.[id]) {
|
|
163
|
+
conformsTo.push(doc.references[id].title || id);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
name,
|
|
171
|
+
type,
|
|
172
|
+
module,
|
|
173
|
+
description,
|
|
174
|
+
declaration,
|
|
175
|
+
availability: platforms,
|
|
176
|
+
examples,
|
|
177
|
+
relatedSymbols,
|
|
178
|
+
conformsTo: conformsTo.length > 0 ? conformsTo : undefined,
|
|
179
|
+
url: `https://swiftipedia.org${path}`,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
async getExamples(path) {
|
|
183
|
+
const doc = await this.fetchDocumentation(path);
|
|
184
|
+
if (!doc.primaryContentSections) {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
return this.extractExamples(doc.primaryContentSections);
|
|
188
|
+
}
|
|
189
|
+
async browseModule(module, categoryFilter, limit = 50) {
|
|
190
|
+
const symbols = await this.loadSymbols();
|
|
191
|
+
let filtered = symbols.filter((symbol) => {
|
|
192
|
+
return symbol.url.includes(`/documentation/${module}/`);
|
|
193
|
+
});
|
|
194
|
+
filtered = filtered.slice(0, limit * 2);
|
|
195
|
+
const results = [];
|
|
196
|
+
for (const symbol of filtered) {
|
|
197
|
+
try {
|
|
198
|
+
const doc = await this.fetchDocumentation(symbol.url);
|
|
199
|
+
const type = doc.metadata?.symbolKind || "symbol";
|
|
200
|
+
if (categoryFilter && type !== categoryFilter) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
const description = doc.abstract ? this.extractText(doc.abstract) : "";
|
|
204
|
+
const platforms = doc.metadata?.platforms?.map((p) => p.name) || [];
|
|
205
|
+
results.push({
|
|
206
|
+
name: symbol.title,
|
|
207
|
+
type,
|
|
208
|
+
module,
|
|
209
|
+
description,
|
|
210
|
+
url: `https://swiftipedia.org${symbol.url}`,
|
|
211
|
+
availability: platforms,
|
|
212
|
+
});
|
|
213
|
+
if (results.length >= limit)
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return results;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import { SwiftipediaAPI } from "./api.js";
|
|
6
|
+
const CDN_BASE = "https://dd5k9dbl5p183.cloudfront.net";
|
|
7
|
+
const server = new Server({
|
|
8
|
+
name: "swiftipedia",
|
|
9
|
+
version: "1.0.0",
|
|
10
|
+
}, {
|
|
11
|
+
capabilities: {
|
|
12
|
+
tools: {},
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
const api = new SwiftipediaAPI(CDN_BASE);
|
|
16
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
17
|
+
return {
|
|
18
|
+
tools: [
|
|
19
|
+
{
|
|
20
|
+
name: "search_symbols",
|
|
21
|
+
description: "Search for SwiftUI/Swift API symbols. Returns matching symbols with name, type, module, description, and URL. Supports fuzzy matching on symbol names and searches in descriptions.",
|
|
22
|
+
inputSchema: {
|
|
23
|
+
type: "object",
|
|
24
|
+
properties: {
|
|
25
|
+
query: {
|
|
26
|
+
type: "string",
|
|
27
|
+
description: "Search query (e.g. 'Button', 'VStack alignment', 'List selection')",
|
|
28
|
+
},
|
|
29
|
+
module: {
|
|
30
|
+
type: "string",
|
|
31
|
+
enum: ["swiftui", "unionchat", "uniontabview", "unionkeychain"],
|
|
32
|
+
description: "Optional: filter by module",
|
|
33
|
+
},
|
|
34
|
+
limit: {
|
|
35
|
+
type: "number",
|
|
36
|
+
description: "Maximum number of results (default: 10)",
|
|
37
|
+
default: 10,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
required: ["query"],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "get_symbol_details",
|
|
45
|
+
description: "Get complete documentation for a symbol including description, parameters, return type, availability, code examples, and related symbols.",
|
|
46
|
+
inputSchema: {
|
|
47
|
+
type: "object",
|
|
48
|
+
properties: {
|
|
49
|
+
path: {
|
|
50
|
+
type: "string",
|
|
51
|
+
description: "Symbol path (e.g. '/documentation/swiftui/button')",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
required: ["path"],
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "get_examples",
|
|
59
|
+
description: "Get all code examples for a symbol. Returns Swift code snippets extracted from the documentation.",
|
|
60
|
+
inputSchema: {
|
|
61
|
+
type: "object",
|
|
62
|
+
properties: {
|
|
63
|
+
path: {
|
|
64
|
+
type: "string",
|
|
65
|
+
description: "Symbol path (e.g. '/documentation/swiftui/button')",
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
required: ["path"],
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: "browse_module",
|
|
73
|
+
description: "List all symbols in a module or category. Useful for discovering available APIs.",
|
|
74
|
+
inputSchema: {
|
|
75
|
+
type: "object",
|
|
76
|
+
properties: {
|
|
77
|
+
module: {
|
|
78
|
+
type: "string",
|
|
79
|
+
enum: ["swiftui", "unionchat", "uniontabview", "unionkeychain"],
|
|
80
|
+
description: "Module name",
|
|
81
|
+
},
|
|
82
|
+
category: {
|
|
83
|
+
type: "string",
|
|
84
|
+
description: "Optional: filter by category (e.g. 'struct', 'protocol', 'method')",
|
|
85
|
+
},
|
|
86
|
+
limit: {
|
|
87
|
+
type: "number",
|
|
88
|
+
description: "Maximum number of results (default: 50)",
|
|
89
|
+
default: 50,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
required: ["module"],
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
99
|
+
const { name, arguments: args } = request.params;
|
|
100
|
+
try {
|
|
101
|
+
switch (name) {
|
|
102
|
+
case "search_symbols": {
|
|
103
|
+
const { query, module, limit = 10 } = args;
|
|
104
|
+
const results = await api.searchSymbols(query, module, limit);
|
|
105
|
+
return {
|
|
106
|
+
content: [
|
|
107
|
+
{
|
|
108
|
+
type: "text",
|
|
109
|
+
text: JSON.stringify(results, null, 2),
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
case "get_symbol_details": {
|
|
115
|
+
const { path } = args;
|
|
116
|
+
const details = await api.getSymbolDetails(path);
|
|
117
|
+
return {
|
|
118
|
+
content: [
|
|
119
|
+
{
|
|
120
|
+
type: "text",
|
|
121
|
+
text: JSON.stringify(details, null, 2),
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
case "get_examples": {
|
|
127
|
+
const { path } = args;
|
|
128
|
+
const examples = await api.getExamples(path);
|
|
129
|
+
return {
|
|
130
|
+
content: [
|
|
131
|
+
{
|
|
132
|
+
type: "text",
|
|
133
|
+
text: JSON.stringify(examples, null, 2),
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
case "browse_module": {
|
|
139
|
+
const { module, category, limit = 50 } = args;
|
|
140
|
+
const symbols = await api.browseModule(module, category, limit);
|
|
141
|
+
return {
|
|
142
|
+
content: [
|
|
143
|
+
{
|
|
144
|
+
type: "text",
|
|
145
|
+
text: JSON.stringify(symbols, null, 2),
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
default:
|
|
151
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
156
|
+
return {
|
|
157
|
+
content: [
|
|
158
|
+
{
|
|
159
|
+
type: "text",
|
|
160
|
+
text: JSON.stringify({ error: errorMessage }, null, 2),
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
isError: true,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
async function main() {
|
|
168
|
+
const transport = new StdioServerTransport();
|
|
169
|
+
await server.connect(transport);
|
|
170
|
+
console.error("Swiftipedia MCP server running on stdio");
|
|
171
|
+
}
|
|
172
|
+
main().catch((error) => {
|
|
173
|
+
console.error("Fatal error:", error);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
});
|
|
176
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "swiftipedia-mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for Swiftipedia documentation",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"swiftipedia-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"start": "node dist/index.js",
|
|
13
|
+
"dev": "tsc --watch",
|
|
14
|
+
"prepare": "tsc"
|
|
15
|
+
},
|
|
16
|
+
"keywords": ["mcp", "swiftui", "swift", "documentation"],
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=18.0.0"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@modelcontextprotocol/sdk": "^1.0.4"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^20.10.5",
|
|
26
|
+
"typescript": "^5.3.3"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
package/src/api.ts
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
interface Symbol {
|
|
2
|
+
title: string;
|
|
3
|
+
url: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
interface SearchResult {
|
|
7
|
+
name: string;
|
|
8
|
+
type: string;
|
|
9
|
+
module: string;
|
|
10
|
+
description: string;
|
|
11
|
+
url: string;
|
|
12
|
+
availability?: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface CodeExample {
|
|
16
|
+
code: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface SymbolDetails {
|
|
21
|
+
name: string;
|
|
22
|
+
type: string;
|
|
23
|
+
module: string;
|
|
24
|
+
description: string;
|
|
25
|
+
declaration: string;
|
|
26
|
+
availability: string[];
|
|
27
|
+
examples: CodeExample[];
|
|
28
|
+
relatedSymbols: { name: string; url: string }[];
|
|
29
|
+
conformsTo?: string[];
|
|
30
|
+
url: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class SwiftipediaAPI {
|
|
34
|
+
private symbolsCache: Symbol[] | null = null;
|
|
35
|
+
private detailsCache = new Map<string, any>();
|
|
36
|
+
private cdnBase: string;
|
|
37
|
+
|
|
38
|
+
constructor(cdnBase: string) {
|
|
39
|
+
this.cdnBase = cdnBase;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private async loadSymbols(): Promise<Symbol[]> {
|
|
43
|
+
if (this.symbolsCache) {
|
|
44
|
+
return this.symbolsCache;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const url = `${this.cdnBase}/symbols.json`;
|
|
48
|
+
const response = await fetch(url);
|
|
49
|
+
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
throw new Error(`Failed to fetch symbols: ${response.statusText}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const data = await response.json() as Symbol[];
|
|
55
|
+
this.symbolsCache = data;
|
|
56
|
+
return this.symbolsCache;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private async fetchDocumentation(path: string): Promise<any> {
|
|
60
|
+
if (this.detailsCache.has(path)) {
|
|
61
|
+
return this.detailsCache.get(path);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let cleanPath = path.replace(/^\/documentation\//, "");
|
|
65
|
+
const url = `${this.cdnBase}/documentation/data/documentation/${cleanPath}.json`;
|
|
66
|
+
|
|
67
|
+
const response = await fetch(url);
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
throw new Error(`Symbol not found: ${path}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const data = await response.json();
|
|
73
|
+
this.detailsCache.set(path, data);
|
|
74
|
+
return data;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private extractText(content: any[]): string {
|
|
78
|
+
if (!content) return "";
|
|
79
|
+
|
|
80
|
+
return content
|
|
81
|
+
.map((item) => {
|
|
82
|
+
if (item.type === "text") return item.text;
|
|
83
|
+
if (item.type === "codeVoice") return `\`${item.code}\``;
|
|
84
|
+
if (item.inlineContent) return this.extractText(item.inlineContent);
|
|
85
|
+
return "";
|
|
86
|
+
})
|
|
87
|
+
.join("");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private extractExamples(sections: any[]): CodeExample[] {
|
|
91
|
+
const examples: CodeExample[] = [];
|
|
92
|
+
|
|
93
|
+
for (const section of sections) {
|
|
94
|
+
if (section.kind === "content" && section.content) {
|
|
95
|
+
for (const item of section.content) {
|
|
96
|
+
if (item.type === "codeListing" && item.syntax === "swift") {
|
|
97
|
+
examples.push({
|
|
98
|
+
code: item.code.join("\n"),
|
|
99
|
+
description: undefined,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return examples;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private calculateRelevance(symbol: Symbol, query: string): number {
|
|
110
|
+
const lowerQuery = query.toLowerCase();
|
|
111
|
+
const lowerTitle = symbol.title.toLowerCase();
|
|
112
|
+
|
|
113
|
+
if (lowerTitle === lowerQuery) return 100;
|
|
114
|
+
if (lowerTitle.startsWith(lowerQuery)) return 90;
|
|
115
|
+
if (lowerTitle.includes(lowerQuery)) return 70;
|
|
116
|
+
|
|
117
|
+
const words = lowerQuery.split(/\s+/);
|
|
118
|
+
const matchedWords = words.filter((word) =>
|
|
119
|
+
lowerTitle.includes(word)
|
|
120
|
+
).length;
|
|
121
|
+
if (matchedWords > 0) return 50 + (matchedWords / words.length) * 20;
|
|
122
|
+
|
|
123
|
+
return 0;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async searchSymbols(
|
|
127
|
+
query: string,
|
|
128
|
+
moduleFilter?: string,
|
|
129
|
+
limit: number = 10
|
|
130
|
+
): Promise<SearchResult[]> {
|
|
131
|
+
const symbols = await this.loadSymbols();
|
|
132
|
+
|
|
133
|
+
let filtered = symbols.filter((symbol) => {
|
|
134
|
+
if (
|
|
135
|
+
moduleFilter &&
|
|
136
|
+
!symbol.url.includes(`/documentation/${moduleFilter}/`)
|
|
137
|
+
) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
return this.calculateRelevance(symbol, query) > 0;
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
filtered.sort((a, b) => {
|
|
144
|
+
return this.calculateRelevance(b, query) - this.calculateRelevance(a, query);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
filtered = filtered.slice(0, limit);
|
|
148
|
+
|
|
149
|
+
const results: SearchResult[] = [];
|
|
150
|
+
for (const symbol of filtered) {
|
|
151
|
+
const module = symbol.url.split("/")[2] || "unknown";
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
const doc = await this.fetchDocumentation(symbol.url);
|
|
155
|
+
const description = doc.abstract ? this.extractText(doc.abstract) : "";
|
|
156
|
+
const type = doc.metadata?.symbolKind || "symbol";
|
|
157
|
+
const platforms = doc.metadata?.platforms?.map((p: any) => p.name) || [];
|
|
158
|
+
|
|
159
|
+
results.push({
|
|
160
|
+
name: symbol.title,
|
|
161
|
+
type,
|
|
162
|
+
module,
|
|
163
|
+
description,
|
|
164
|
+
url: `https://swiftipedia.org${symbol.url}`,
|
|
165
|
+
availability: platforms,
|
|
166
|
+
});
|
|
167
|
+
} catch (error) {
|
|
168
|
+
results.push({
|
|
169
|
+
name: symbol.title,
|
|
170
|
+
type: "unknown",
|
|
171
|
+
module,
|
|
172
|
+
description: "",
|
|
173
|
+
url: `https://swiftipedia.org${symbol.url}`,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return results;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async getSymbolDetails(path: string): Promise<SymbolDetails> {
|
|
182
|
+
const doc = await this.fetchDocumentation(path);
|
|
183
|
+
|
|
184
|
+
const name = doc.metadata?.title || "Unknown";
|
|
185
|
+
const type = doc.metadata?.symbolKind || "symbol";
|
|
186
|
+
const module = path.split("/")[2] || "unknown";
|
|
187
|
+
const description = doc.abstract ? this.extractText(doc.abstract) : "";
|
|
188
|
+
|
|
189
|
+
let declaration = "";
|
|
190
|
+
if (doc.primaryContentSections) {
|
|
191
|
+
for (const section of doc.primaryContentSections) {
|
|
192
|
+
if (section.kind === "declarations" && section.declarations?.[0]?.tokens) {
|
|
193
|
+
declaration = section.declarations[0].tokens.map((t: any) => t.text).join("");
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const examples = doc.primaryContentSections
|
|
200
|
+
? this.extractExamples(doc.primaryContentSections)
|
|
201
|
+
: [];
|
|
202
|
+
|
|
203
|
+
const platforms =
|
|
204
|
+
doc.metadata?.platforms?.map(
|
|
205
|
+
(p: any) =>
|
|
206
|
+
`${p.name} ${p.introducedAt}${p.deprecated ? " (deprecated)" : ""}`
|
|
207
|
+
) || [];
|
|
208
|
+
|
|
209
|
+
const relatedSymbols: { name: string; url: string }[] = [];
|
|
210
|
+
if (doc.topicSections) {
|
|
211
|
+
for (const section of doc.topicSections) {
|
|
212
|
+
if (section.identifiers) {
|
|
213
|
+
for (const id of section.identifiers.slice(0, 5)) {
|
|
214
|
+
if (doc.references?.[id]) {
|
|
215
|
+
const ref = doc.references[id];
|
|
216
|
+
relatedSymbols.push({
|
|
217
|
+
name: ref.title || ref.name || id,
|
|
218
|
+
url: `https://swiftipedia.org${ref.url || ""}`,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const conformsTo: string[] = [];
|
|
227
|
+
if (doc.relationshipsSections) {
|
|
228
|
+
for (const section of doc.relationshipsSections) {
|
|
229
|
+
if (section.type === "conformsTo" && section.identifiers) {
|
|
230
|
+
for (const id of section.identifiers) {
|
|
231
|
+
if (doc.references?.[id]) {
|
|
232
|
+
conformsTo.push(doc.references[id].title || id);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
name,
|
|
241
|
+
type,
|
|
242
|
+
module,
|
|
243
|
+
description,
|
|
244
|
+
declaration,
|
|
245
|
+
availability: platforms,
|
|
246
|
+
examples,
|
|
247
|
+
relatedSymbols,
|
|
248
|
+
conformsTo: conformsTo.length > 0 ? conformsTo : undefined,
|
|
249
|
+
url: `https://swiftipedia.org${path}`,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async getExamples(path: string): Promise<CodeExample[]> {
|
|
254
|
+
const doc = await this.fetchDocumentation(path);
|
|
255
|
+
|
|
256
|
+
if (!doc.primaryContentSections) {
|
|
257
|
+
return [];
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return this.extractExamples(doc.primaryContentSections);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async browseModule(
|
|
264
|
+
module: string,
|
|
265
|
+
categoryFilter?: string,
|
|
266
|
+
limit: number = 50
|
|
267
|
+
): Promise<SearchResult[]> {
|
|
268
|
+
const symbols = await this.loadSymbols();
|
|
269
|
+
|
|
270
|
+
let filtered = symbols.filter((symbol) => {
|
|
271
|
+
return symbol.url.includes(`/documentation/${module}/`);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
filtered = filtered.slice(0, limit * 2);
|
|
275
|
+
|
|
276
|
+
const results: SearchResult[] = [];
|
|
277
|
+
for (const symbol of filtered) {
|
|
278
|
+
try {
|
|
279
|
+
const doc = await this.fetchDocumentation(symbol.url);
|
|
280
|
+
const type = doc.metadata?.symbolKind || "symbol";
|
|
281
|
+
|
|
282
|
+
if (categoryFilter && type !== categoryFilter) {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const description = doc.abstract ? this.extractText(doc.abstract) : "";
|
|
287
|
+
const platforms = doc.metadata?.platforms?.map((p: any) => p.name) || [];
|
|
288
|
+
|
|
289
|
+
results.push({
|
|
290
|
+
name: symbol.title,
|
|
291
|
+
type,
|
|
292
|
+
module,
|
|
293
|
+
description,
|
|
294
|
+
url: `https://swiftipedia.org${symbol.url}`,
|
|
295
|
+
availability: platforms,
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
if (results.length >= limit) break;
|
|
299
|
+
} catch (error) {
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return results;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import {
|
|
6
|
+
CallToolRequestSchema,
|
|
7
|
+
ListToolsRequestSchema,
|
|
8
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
import { SwiftipediaAPI } from "./api.js";
|
|
10
|
+
|
|
11
|
+
const CDN_BASE = "https://dd5k9dbl5p183.cloudfront.net";
|
|
12
|
+
|
|
13
|
+
const server = new Server(
|
|
14
|
+
{
|
|
15
|
+
name: "swiftipedia",
|
|
16
|
+
version: "1.0.0",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
capabilities: {
|
|
20
|
+
tools: {},
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const api = new SwiftipediaAPI(CDN_BASE);
|
|
26
|
+
|
|
27
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
28
|
+
return {
|
|
29
|
+
tools: [
|
|
30
|
+
{
|
|
31
|
+
name: "search_symbols",
|
|
32
|
+
description:
|
|
33
|
+
"Search for SwiftUI/Swift API symbols. Returns matching symbols with name, type, module, description, and URL. Supports fuzzy matching on symbol names and searches in descriptions.",
|
|
34
|
+
inputSchema: {
|
|
35
|
+
type: "object",
|
|
36
|
+
properties: {
|
|
37
|
+
query: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description:
|
|
40
|
+
"Search query (e.g. 'Button', 'VStack alignment', 'List selection')",
|
|
41
|
+
},
|
|
42
|
+
module: {
|
|
43
|
+
type: "string",
|
|
44
|
+
enum: ["swiftui", "unionchat", "uniontabview", "unionkeychain"],
|
|
45
|
+
description: "Optional: filter by module",
|
|
46
|
+
},
|
|
47
|
+
limit: {
|
|
48
|
+
type: "number",
|
|
49
|
+
description: "Maximum number of results (default: 10)",
|
|
50
|
+
default: 10,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
required: ["query"],
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: "get_symbol_details",
|
|
58
|
+
description:
|
|
59
|
+
"Get complete documentation for a symbol including description, parameters, return type, availability, code examples, and related symbols.",
|
|
60
|
+
inputSchema: {
|
|
61
|
+
type: "object",
|
|
62
|
+
properties: {
|
|
63
|
+
path: {
|
|
64
|
+
type: "string",
|
|
65
|
+
description:
|
|
66
|
+
"Symbol path (e.g. '/documentation/swiftui/button')",
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
required: ["path"],
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: "get_examples",
|
|
74
|
+
description:
|
|
75
|
+
"Get all code examples for a symbol. Returns Swift code snippets extracted from the documentation.",
|
|
76
|
+
inputSchema: {
|
|
77
|
+
type: "object",
|
|
78
|
+
properties: {
|
|
79
|
+
path: {
|
|
80
|
+
type: "string",
|
|
81
|
+
description:
|
|
82
|
+
"Symbol path (e.g. '/documentation/swiftui/button')",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
required: ["path"],
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: "browse_module",
|
|
90
|
+
description:
|
|
91
|
+
"List all symbols in a module or category. Useful for discovering available APIs.",
|
|
92
|
+
inputSchema: {
|
|
93
|
+
type: "object",
|
|
94
|
+
properties: {
|
|
95
|
+
module: {
|
|
96
|
+
type: "string",
|
|
97
|
+
enum: ["swiftui", "unionchat", "uniontabview", "unionkeychain"],
|
|
98
|
+
description: "Module name",
|
|
99
|
+
},
|
|
100
|
+
category: {
|
|
101
|
+
type: "string",
|
|
102
|
+
description:
|
|
103
|
+
"Optional: filter by category (e.g. 'struct', 'protocol', 'method')",
|
|
104
|
+
},
|
|
105
|
+
limit: {
|
|
106
|
+
type: "number",
|
|
107
|
+
description: "Maximum number of results (default: 50)",
|
|
108
|
+
default: 50,
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
required: ["module"],
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
119
|
+
const { name, arguments: args } = request.params;
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
switch (name) {
|
|
123
|
+
case "search_symbols": {
|
|
124
|
+
const { query, module, limit = 10 } = args as any;
|
|
125
|
+
const results = await api.searchSymbols(query, module, limit);
|
|
126
|
+
return {
|
|
127
|
+
content: [
|
|
128
|
+
{
|
|
129
|
+
type: "text",
|
|
130
|
+
text: JSON.stringify(results, null, 2),
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
case "get_symbol_details": {
|
|
137
|
+
const { path } = args as any;
|
|
138
|
+
const details = await api.getSymbolDetails(path);
|
|
139
|
+
return {
|
|
140
|
+
content: [
|
|
141
|
+
{
|
|
142
|
+
type: "text",
|
|
143
|
+
text: JSON.stringify(details, null, 2),
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
case "get_examples": {
|
|
150
|
+
const { path } = args as any;
|
|
151
|
+
const examples = await api.getExamples(path);
|
|
152
|
+
return {
|
|
153
|
+
content: [
|
|
154
|
+
{
|
|
155
|
+
type: "text",
|
|
156
|
+
text: JSON.stringify(examples, null, 2),
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
case "browse_module": {
|
|
163
|
+
const { module, category, limit = 50 } = args as any;
|
|
164
|
+
const symbols = await api.browseModule(module, category, limit);
|
|
165
|
+
return {
|
|
166
|
+
content: [
|
|
167
|
+
{
|
|
168
|
+
type: "text",
|
|
169
|
+
text: JSON.stringify(symbols, null, 2),
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
default:
|
|
176
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
177
|
+
}
|
|
178
|
+
} catch (error) {
|
|
179
|
+
const errorMessage =
|
|
180
|
+
error instanceof Error ? error.message : String(error);
|
|
181
|
+
return {
|
|
182
|
+
content: [
|
|
183
|
+
{
|
|
184
|
+
type: "text",
|
|
185
|
+
text: JSON.stringify({ error: errorMessage }, null, 2),
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
isError: true,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
async function main() {
|
|
194
|
+
const transport = new StdioServerTransport();
|
|
195
|
+
await server.connect(transport);
|
|
196
|
+
console.error("Swiftipedia MCP server running on stdio");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
main().catch((error) => {
|
|
200
|
+
console.error("Fatal error:", error);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ES2020",
|
|
5
|
+
"lib": ["ES2020"],
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"moduleResolution": "node",
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"declaration": true,
|
|
15
|
+
"declarationMap": true,
|
|
16
|
+
"sourceMap": true
|
|
17
|
+
},
|
|
18
|
+
"include": ["src/**/*"],
|
|
19
|
+
"exclude": ["node_modules", "dist"]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|