337 lines
9.2 KiB
JavaScript
337 lines
9.2 KiB
JavaScript
|
|
#!/usr/bin/env node
|
||
|
|
// MCP Server - exposes dalidou tools to Claude Code on Windows
|
||
|
|
|
||
|
|
import "dotenv/config";
|
||
|
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
||
|
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||
|
|
import {
|
||
|
|
CallToolRequestSchema,
|
||
|
|
ListToolsRequestSchema,
|
||
|
|
} from "@modelcontextprotocol/sdk/types.js";
|
||
|
|
import weather from "../tools/weather.js";
|
||
|
|
import gitea from "../tools/gitea.js";
|
||
|
|
import siemensDocs from "../tools/siemens-docs.js";
|
||
|
|
|
||
|
|
const server = new Server(
|
||
|
|
{
|
||
|
|
name: "dalidou-assistant",
|
||
|
|
version: "1.3.0",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
capabilities: {
|
||
|
|
tools: {},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
);
|
||
|
|
|
||
|
|
// List available tools
|
||
|
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||
|
|
return {
|
||
|
|
tools: [
|
||
|
|
// Weather tools
|
||
|
|
{
|
||
|
|
name: "get_current_weather",
|
||
|
|
description: "Get current weather conditions in Rouyn-Noranda (temperature, wind, conditions)",
|
||
|
|
inputSchema: {
|
||
|
|
type: "object",
|
||
|
|
properties: {},
|
||
|
|
required: []
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "get_hourly_forecast",
|
||
|
|
description: "Get hourly weather forecast for planning activities",
|
||
|
|
inputSchema: {
|
||
|
|
type: "object",
|
||
|
|
properties: {
|
||
|
|
days: { type: "number", description: "Days to forecast (1-7)" }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "get_daily_forecast",
|
||
|
|
description: "Get daily weather forecast (highs, lows, conditions)",
|
||
|
|
inputSchema: {
|
||
|
|
type: "object",
|
||
|
|
properties: {
|
||
|
|
days: { type: "number", description: "Days to forecast (1-14)" }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "find_best_activity_windows",
|
||
|
|
description: "Find best time windows for outdoor activities based on weather",
|
||
|
|
inputSchema: {
|
||
|
|
type: "object",
|
||
|
|
properties: {
|
||
|
|
activity: {
|
||
|
|
type: "string",
|
||
|
|
description: "Activity: skiing, cross-country-skiing, hiking, mushroom-hunting"
|
||
|
|
},
|
||
|
|
days: { type: "number", description: "Days to search (1-7)" }
|
||
|
|
},
|
||
|
|
required: ["activity"]
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "check_skiing_conditions",
|
||
|
|
description: "Check if current conditions are good for cross-country skiing",
|
||
|
|
inputSchema: {
|
||
|
|
type: "object",
|
||
|
|
properties: {}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
// Gitea tools
|
||
|
|
{
|
||
|
|
name: "get_today_commits",
|
||
|
|
description: "Get all git commits made today from Gitea",
|
||
|
|
inputSchema: {
|
||
|
|
type: "object",
|
||
|
|
properties: {}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "get_recent_activity",
|
||
|
|
description: "Get recent git activity across all Gitea repos",
|
||
|
|
inputSchema: {
|
||
|
|
type: "object",
|
||
|
|
properties: {
|
||
|
|
days: { type: "number", description: "Days of history (default 7)" }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "list_repos",
|
||
|
|
description: "List all repositories on Gitea",
|
||
|
|
inputSchema: {
|
||
|
|
type: "object",
|
||
|
|
properties: {}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
// Siemens Documentation tools
|
||
|
|
{
|
||
|
|
name: "siemens_docs_list",
|
||
|
|
description: "List available Siemens documentation categories, NX Open class IDs, and usage info",
|
||
|
|
inputSchema: {
|
||
|
|
type: "object",
|
||
|
|
properties: {}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "siemens_docs_search",
|
||
|
|
description: "Search Siemens Support Center documentation for a specific topic",
|
||
|
|
inputSchema: {
|
||
|
|
type: "object",
|
||
|
|
properties: {
|
||
|
|
query: {
|
||
|
|
type: "string",
|
||
|
|
description: "Search query (e.g., 'NXOpen Body.GetFaces', 'sketch constraints')"
|
||
|
|
},
|
||
|
|
product: {
|
||
|
|
type: "string",
|
||
|
|
description: "Product to search: nx, teamcenter, simcenter, nastran (default: nx)"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
required: ["query"]
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "siemens_docs_fetch",
|
||
|
|
description: "Fetch a specific Siemens documentation page by URL",
|
||
|
|
inputSchema: {
|
||
|
|
type: "object",
|
||
|
|
properties: {
|
||
|
|
url: {
|
||
|
|
type: "string",
|
||
|
|
description: "Full URL of the documentation page to fetch"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
required: ["url"]
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "siemens_auth_status",
|
||
|
|
description: "Check Siemens Support Center authentication status",
|
||
|
|
inputSchema: {
|
||
|
|
type: "object",
|
||
|
|
properties: {}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "siemens_login",
|
||
|
|
description: "Login to Siemens Support Center (use if session expired)",
|
||
|
|
inputSchema: {
|
||
|
|
type: "object",
|
||
|
|
properties: {}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
// NX Open Python Reference tools
|
||
|
|
{
|
||
|
|
name: "nxopen_get_class",
|
||
|
|
description: "Get NX Open Python class documentation. Known classes: Session, Part, BasePart, PdmSession, CaeSession",
|
||
|
|
inputSchema: {
|
||
|
|
type: "object",
|
||
|
|
properties: {
|
||
|
|
className: {
|
||
|
|
type: "string",
|
||
|
|
description: "Class name (e.g., 'Session', 'Part', 'BasePart')"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
required: ["className"]
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "nxopen_get_index",
|
||
|
|
description: "Get NX Open Python Reference index pages (class list, functions, hierarchy)",
|
||
|
|
inputSchema: {
|
||
|
|
type: "object",
|
||
|
|
properties: {
|
||
|
|
indexType: {
|
||
|
|
type: "string",
|
||
|
|
description: "Index type: classes, annotated, hierarchy, functions, main (default: classes)"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "nxopen_fetch_page",
|
||
|
|
description: "Fetch any NX Open Python Reference page by page ID (e.g., 'a03318.html' for Session)",
|
||
|
|
inputSchema: {
|
||
|
|
type: "object",
|
||
|
|
properties: {
|
||
|
|
pagePath: {
|
||
|
|
type: "string",
|
||
|
|
description: "Page path (e.g., 'a03318.html', 'classes.html', 'functions_s.html')"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
required: ["pagePath"]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
]
|
||
|
|
};
|
||
|
|
});
|
||
|
|
|
||
|
|
// Handle tool calls
|
||
|
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||
|
|
const { name, arguments: args } = request.params;
|
||
|
|
|
||
|
|
try {
|
||
|
|
let result;
|
||
|
|
|
||
|
|
switch (name) {
|
||
|
|
// Weather tools
|
||
|
|
case "get_current_weather":
|
||
|
|
result = await weather.getCurrentWeather();
|
||
|
|
break;
|
||
|
|
|
||
|
|
case "get_hourly_forecast":
|
||
|
|
result = await weather.getHourlyForecast(args?.days || 2);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case "get_daily_forecast":
|
||
|
|
result = await weather.getDailyForecast(args?.days || 7);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case "find_best_activity_windows":
|
||
|
|
result = await weather.findBestTimeWindows(args.activity, args?.days || 2);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case "check_skiing_conditions":
|
||
|
|
const current = await weather.getCurrentWeather();
|
||
|
|
const skiCheck = weather.isGoodForSkiing(current);
|
||
|
|
result = {
|
||
|
|
current_weather: current,
|
||
|
|
good_for_skiing: skiCheck.good,
|
||
|
|
assessment: skiCheck.reasons
|
||
|
|
};
|
||
|
|
break;
|
||
|
|
|
||
|
|
// Gitea tools
|
||
|
|
case "get_today_commits":
|
||
|
|
result = await gitea.getAllTodayCommits();
|
||
|
|
break;
|
||
|
|
|
||
|
|
case "get_recent_activity":
|
||
|
|
result = await gitea.getRepoActivity(args?.days || 7);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case "list_repos":
|
||
|
|
result = await gitea.listRepos();
|
||
|
|
break;
|
||
|
|
|
||
|
|
// Siemens Documentation tools
|
||
|
|
case "siemens_docs_list":
|
||
|
|
result = await siemensDocs.listAvailableDocs();
|
||
|
|
break;
|
||
|
|
|
||
|
|
case "siemens_docs_search":
|
||
|
|
result = await siemensDocs.searchDocs(args.query, args.product || "nx");
|
||
|
|
break;
|
||
|
|
|
||
|
|
case "siemens_docs_fetch":
|
||
|
|
result = await siemensDocs.fetchDocPage(args.url);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case "siemens_auth_status":
|
||
|
|
result = await siemensDocs.checkAuthStatus();
|
||
|
|
break;
|
||
|
|
|
||
|
|
case "siemens_login":
|
||
|
|
result = await siemensDocs.performLogin();
|
||
|
|
break;
|
||
|
|
|
||
|
|
// NX Open Python Reference tools
|
||
|
|
case "nxopen_get_class":
|
||
|
|
result = await siemensDocs.fetchNXOpenClass(args.className);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case "nxopen_get_index":
|
||
|
|
result = await siemensDocs.getNXOpenIndex(args?.indexType || "classes");
|
||
|
|
break;
|
||
|
|
|
||
|
|
case "nxopen_fetch_page":
|
||
|
|
result = await siemensDocs.fetchNXOpenPythonRef(args.pagePath);
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
return {
|
||
|
|
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
||
|
|
isError: true
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
content: [{
|
||
|
|
type: "text",
|
||
|
|
text: typeof result === "string" ? result : JSON.stringify(result, null, 2)
|
||
|
|
}]
|
||
|
|
};
|
||
|
|
|
||
|
|
} catch (error) {
|
||
|
|
return {
|
||
|
|
content: [{ type: "text", text: `Error: ${error.message}` }],
|
||
|
|
isError: true
|
||
|
|
};
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Cleanup on exit
|
||
|
|
process.on("SIGINT", async () => {
|
||
|
|
await siemensDocs.closeBrowser();
|
||
|
|
process.exit(0);
|
||
|
|
});
|
||
|
|
|
||
|
|
process.on("SIGTERM", async () => {
|
||
|
|
await siemensDocs.closeBrowser();
|
||
|
|
process.exit(0);
|
||
|
|
});
|
||
|
|
|
||
|
|
// Start server
|
||
|
|
async function main() {
|
||
|
|
const transport = new StdioServerTransport();
|
||
|
|
await server.connect(transport);
|
||
|
|
console.error("Dalidou MCP Server v1.3.0 running on stdio (with NX Open Python Reference)");
|
||
|
|
}
|
||
|
|
|
||
|
|
main().catch(console.error);
|