snow-flow
Version:
Snow-Flow v3.2.0: Complete ServiceNow Enterprise Suite with 180+ MCP Tools. ATF Testing, Knowledge Management, Service Catalog, Change Management with CAB scheduling, Virtual Agent chatbots with NLU, Performance Analytics KPIs, Flow Designer automation, A
648 lines • 25.4 kB
JavaScript
;
/**
* ServiceNow MCP Server
* Provides Claude Code with direct access to ServiceNow APIs via MCP protocol
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServiceNowMCPServer = void 0;
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
const servicenow_client_js_1 = require("../utils/servicenow-client.js");
const snow_oauth_js_1 = require("../utils/snow-oauth.js");
class ServiceNowMCPServer {
constructor(config) {
this.isAuthenticated = false;
this.config = config;
this.oauth = new snow_oauth_js_1.ServiceNowOAuth();
this.snowClient = new servicenow_client_js_1.ServiceNowClient();
this.server = new index_js_1.Server({
name: config.name,
version: config.version,
}, {
capabilities: {
tools: {}
}
});
this.setupToolHandlers();
this.setupRequestHandlers();
}
async checkAuthentication() {
try {
this.isAuthenticated = await this.oauth.isAuthenticated();
return this.isAuthenticated;
}
catch (error) {
console.error('Authentication check failed:', error);
return false;
}
}
setupRequestHandlers() {
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
const isAuth = await this.checkAuthentication();
const tools = [
{
name: "snow_auth_status",
description: "Check ServiceNow authentication status",
inputSchema: {
type: "object",
properties: {},
required: []
}
}
];
if (isAuth) {
tools.push({
name: "snow_create_widget",
description: "Create a new ServiceNow Service Portal widget",
inputSchema: {
type: "object",
properties: {
name: {
type: "string",
description: "Widget name"
},
id: {
type: "string",
description: "Widget ID (unique identifier)"
},
title: {
type: "string",
description: "Widget display title"
},
description: {
type: "string",
description: "Widget description"
},
template: {
type: "string",
description: "HTML template for the widget"
},
css: {
type: "string",
description: "CSS styling for the widget"
},
client_script: {
type: "string",
description: "Client-side AngularJS script"
},
server_script: {
type: "string",
description: "Server-side script"
},
category: {
type: "string",
description: "Widget category (e.g., 'incident', 'custom')"
}
},
required: ["name", "id", "title", "description", "template", "css", "client_script", "server_script"]
}
}, {
name: "snow_update_widget",
description: "Update an existing ServiceNow widget",
inputSchema: {
type: "object",
properties: {
sys_id: {
type: "string",
description: "System ID of the widget to update"
},
updates: {
type: "object",
description: "Object containing fields to update",
properties: {
name: { type: "string" },
title: { type: "string" },
description: { type: "string" },
template: { type: "string" },
css: { type: "string" },
client_script: { type: "string" },
server_script: { type: "string" }
}
}
},
required: ["sys_id", "updates"]
}
}, {
name: "snow_get_widget",
description: "Get details of a ServiceNow widget by ID",
inputSchema: {
type: "object",
properties: {
widget_id: {
type: "string",
description: "Widget ID to retrieve"
}
},
required: ["widget_id"]
}
}, {
name: "snow_list_widgets",
description: "List all ServiceNow widgets",
inputSchema: {
type: "object",
properties: {
limit: {
type: "number",
description: "Maximum number of widgets to return (default: 50)"
}
},
required: []
}
}, {
name: "snow_create_workflow",
description: "Create a new ServiceNow workflow",
inputSchema: {
type: "object",
properties: {
name: {
type: "string",
description: "Workflow name"
},
description: {
type: "string",
description: "Workflow description"
},
active: {
type: "boolean",
description: "Whether workflow is active"
},
workflow_version: {
type: "string",
description: "Workflow version"
},
table: {
type: "string",
description: "Table this workflow applies to"
},
condition: {
type: "string",
description: "Workflow activation condition"
}
},
required: ["name", "description", "active", "workflow_version"]
}
}, {
name: "snow_execute_script",
description: "Execute a server-side script in ServiceNow",
inputSchema: {
type: "object",
properties: {
script: {
type: "string",
description: "JavaScript code to execute"
},
description: {
type: "string",
description: "Description of what the script does"
}
},
required: ["script"]
}
}, {
name: "snow_test_connection",
description: "Test connection to ServiceNow and get current user info",
inputSchema: {
type: "object",
properties: {},
required: []
}
}, {
name: "snow_get_instance_info",
description: "Get ServiceNow instance information",
inputSchema: {
type: "object",
properties: {},
required: []
}
});
}
return { tools };
});
}
setupToolHandlers() {
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case "snow_auth_status":
return await this.handleAuthStatus();
case "snow_create_widget":
return await this.handleCreateWidget(args);
case "snow_update_widget":
return await this.handleUpdateWidget(args);
case "snow_get_widget":
return await this.handleGetWidget(args);
case "snow_list_widgets":
return await this.handleListWidgets(args);
case "snow_create_workflow":
return await this.handleCreateWorkflow(args);
case "snow_execute_script":
return await this.handleExecuteScript(args);
case "snow_test_connection":
return await this.handleTestConnection();
case "snow_get_instance_info":
return await this.handleGetInstanceInfo();
default:
throw new Error(`Unknown tool: ${name}`);
}
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
content: [
{
type: "text",
text: `Error executing ${name}: ${errorMessage}`
}
]
};
}
});
}
async handleAuthStatus() {
const isAuth = await this.checkAuthentication();
const credentials = await this.oauth.loadCredentials();
let statusText = "🔐 ServiceNow Authentication Status:\n\n";
if (isAuth && credentials) {
statusText += `✅ Status: Authenticated\n`;
statusText += `🏢 Instance: ${credentials.instance}\n`;
statusText += `🔑 Client ID: ${credentials.clientId}\n`;
statusText += `📅 Expires: ${credentials.expiresAt ? new Date(credentials.expiresAt).toLocaleString() : 'Unknown'}\n`;
}
else {
statusText += `❌ Status: Not authenticated\n`;
statusText += `💡 Run "snow-flow auth login" to authenticate\n`;
}
return {
content: [
{
type: "text",
text: statusText
}
]
};
}
async handleCreateWidget(args) {
if (!await this.checkAuthentication()) {
return {
content: [
{
type: "text",
text: "❌ Not authenticated. Please run 'snow-flow auth login' first."
}
]
};
}
const result = await this.snowClient.createWidget({
name: args.name,
id: args.id,
title: args.title,
description: args.description,
template: args.template,
css: args.css,
client_script: args.client_script,
server_script: args.server_script,
category: args.category || 'custom'
});
if (result.success) {
const credentials = await this.oauth.loadCredentials();
const instanceUrl = `https://${credentials?.instance}`;
const widgetUrl = `${instanceUrl}/sp_config/?id=widget_editor&widget_id=${result.data?.sys_id}`;
return {
content: [
{
type: "text",
text: `✅ Widget created successfully!\n\n` +
`🆔 Widget ID: ${result.data?.sys_id}\n` +
`📛 Name: ${result.data?.name}\n` +
`🔗 Edit Widget: ${widgetUrl}\n` +
`🌐 Instance: ${instanceUrl}\n\n` +
`The widget has been created in your ServiceNow instance and is ready for testing!`
}
]
};
}
else {
return {
content: [
{
type: "text",
text: `❌ Failed to create widget: ${result.error}`
}
]
};
}
}
async handleUpdateWidget(args) {
if (!await this.checkAuthentication()) {
return {
content: [
{
type: "text",
text: "❌ Not authenticated. Please run 'snow-flow auth login' first."
}
]
};
}
const result = await this.snowClient.updateWidget(args.sys_id, args.updates);
if (result.success) {
const credentials = await this.oauth.loadCredentials();
const instanceUrl = `https://${credentials?.instance}`;
const widgetUrl = `${instanceUrl}/sp_config/?id=widget_editor&widget_id=${args.sys_id}`;
return {
content: [
{
type: "text",
text: `✅ Widget updated successfully!\n\n` +
`🆔 Widget ID: ${args.sys_id}\n` +
`🔗 Edit Widget: ${widgetUrl}\n` +
`🌐 Instance: ${instanceUrl}\n\n` +
`The widget has been updated in your ServiceNow instance!`
}
]
};
}
else {
return {
content: [
{
type: "text",
text: `❌ Failed to update widget: ${result.error}`
}
]
};
}
}
async handleGetWidget(args) {
if (!await this.checkAuthentication()) {
return {
content: [
{
type: "text",
text: "❌ Not authenticated. Please run 'snow-flow auth login' first."
}
]
};
}
const result = await this.snowClient.getWidget(args.widget_id);
if (result.success) {
return {
content: [
{
type: "text",
text: `✅ Widget found!\n\n` +
`🆔 System ID: ${result.data?.sys_id}\n` +
`📛 Name: ${result.data?.name}\n` +
`🏷️ ID: ${result.data?.id}\n` +
`📝 Title: ${result.data?.title}\n` +
`📄 Description: ${result.data?.description}\n` +
`🏷️ Category: ${result.data?.category}\n\n` +
`Template:\n${result.data?.template}\n\n` +
`CSS:\n${result.data?.css}\n\n` +
`Client Script:\n${result.data?.client_script}\n\n` +
`Server Script:\n${result.data?.server_script}`
}
]
};
}
else {
return {
content: [
{
type: "text",
text: `❌ Widget not found: ${result.error}`
}
]
};
}
}
async handleListWidgets(args) {
if (!await this.checkAuthentication()) {
return {
content: [
{
type: "text",
text: "❌ Not authenticated. Please run 'snow-flow auth login' first."
}
]
};
}
const result = await this.snowClient.getWidgets();
if (result.success && result.result) {
const widgets = result.result.slice(0, args.limit || 50);
let widgetList = `✅ Found ${widgets.length} widgets:\n\n`;
widgets.forEach((widget, index) => {
widgetList += `${index + 1}. ${widget.name} (${widget.id})\n`;
widgetList += ` 🆔 System ID: ${widget.sys_id}\n`;
widgetList += ` 📝 Title: ${widget.title}\n`;
widgetList += ` 📄 Description: ${widget.description}\n`;
widgetList += ` 🏷️ Category: ${widget.category}\n\n`;
});
return {
content: [
{
type: "text",
text: widgetList
}
]
};
}
else {
return {
content: [
{
type: "text",
text: `❌ Failed to list widgets: ${result.error}`
}
]
};
}
}
async handleCreateWorkflow(args) {
if (!await this.checkAuthentication()) {
return {
content: [
{
type: "text",
text: "❌ Not authenticated. Please run 'snow-flow auth login' first."
}
]
};
}
const result = await this.snowClient.createWorkflow({
name: args.name,
description: args.description,
active: args.active,
workflow_version: args.workflow_version,
table: args.table,
condition: args.condition
});
if (result.success) {
const credentials = await this.oauth.loadCredentials();
const instanceUrl = `https://${credentials?.instance}`;
return {
content: [
{
type: "text",
text: `✅ Workflow created successfully!\n\n` +
`🆔 Workflow ID: ${result.data?.sys_id}\n` +
`📛 Name: ${result.data?.name}\n` +
`🌐 Instance: ${instanceUrl}\n\n` +
`The workflow has been created in your ServiceNow instance!`
}
]
};
}
else {
return {
content: [
{
type: "text",
text: `❌ Failed to create workflow: ${result.error}`
}
]
};
}
}
async handleExecuteScript(args) {
if (!await this.checkAuthentication()) {
return {
content: [
{
type: "text",
text: "❌ Not authenticated. Please run 'snow-flow auth login' first."
}
]
};
}
const result = await this.snowClient.executeScript(args.script);
if (result.success) {
return {
content: [
{
type: "text",
text: `✅ Script executed successfully!\n\n` +
`📄 Description: ${args.description || 'No description provided'}\n` +
`⚡ Script:\n${args.script}\n\n` +
`📊 Result:\n${JSON.stringify(result.data, null, 2)}`
}
]
};
}
else {
return {
content: [
{
type: "text",
text: `❌ Failed to execute script: ${result.error}`
}
]
};
}
}
async handleTestConnection() {
if (!await this.checkAuthentication()) {
return {
content: [
{
type: "text",
text: "❌ Not authenticated. Please run 'snow-flow auth login' first."
}
]
};
}
const result = await this.snowClient.testConnection();
if (result.success) {
return {
content: [
{
type: "text",
text: `✅ Connection test successful!\n\n` +
`👤 User: ${result.data?.name} (${result.data?.user_name})\n` +
`📧 Email: ${result.data?.email}\n` +
`🏢 Company: ${result.data?.company?.display_value || 'N/A'}\n` +
`🎭 Role: ${result.data?.title || 'N/A'}\n` +
`📅 Last Login: ${result.data?.last_login_time || 'N/A'}\n\n` +
`ServiceNow connection is working properly!`
}
]
};
}
else {
return {
content: [
{
type: "text",
text: `❌ Connection test failed: ${result.error}`
}
]
};
}
}
async handleGetInstanceInfo() {
if (!await this.checkAuthentication()) {
return {
content: [
{
type: "text",
text: "❌ Not authenticated. Please run 'snow-flow auth login' first."
}
]
};
}
const result = await this.snowClient.getInstanceInfo();
if (result.success) {
const credentials = await this.oauth.loadCredentials();
return {
content: [
{
type: "text",
text: `✅ Instance information retrieved!\n\n` +
`🏢 Instance: ${credentials?.instance}\n` +
`🌐 URL: https://${credentials?.instance}\n` +
`📊 Property: ${result.data?.name}\n` +
`💾 Value: ${result.data?.value}\n\n` +
`Instance is accessible and responding!`
}
]
};
}
else {
return {
content: [
{
type: "text",
text: `❌ Failed to get instance info: ${result.error}`
}
]
};
}
}
async run() {
const transport = new stdio_js_1.StdioServerTransport();
await this.server.connect(transport);
// Keep the server running
await new Promise((resolve) => {
process.on('SIGINT', () => {
console.log('\nServiceNow MCP Server shutting down...');
resolve();
});
});
}
}
exports.ServiceNowMCPServer = ServiceNowMCPServer;
// CLI entry point
async function main() {
const config = {
name: "servicenow-mcp-server",
version: "1.0.0"
};
const server = new ServiceNowMCPServer(config);
await server.run();
}
if (require.main === module) {
main().catch(console.error);
}
//# sourceMappingURL=servicenow-mcp-server.js.map