mcpField
F
fluent-sdk

fluent-sdk

github.com/happy-technologies-llc开发4142 次安装2 天前更新
代码仓库

关于 fluent-sdk

此技能为 ServiceNow 开发提供 NowSDK Fluent、MCP REST 和 Fix Script 三层操作策略,适用于构建和部署 ServiceNow 应用的开发人员。它根据操作类型选择工具,支持版本控制部署,并处理实时数据操作等 Fluent 未覆盖的任务。

主要功能

  1. 01按任务选择 Fluent、MCP REST 或 Fix Script
  2. 02以 TypeScript 定义应用元数据
  3. 03构建并部署 scoped app
  4. 04通过 MCP 查询和更新实时记录
  5. 05处理流程编译等需 Fix Script 的操作

适用场景

  1. 01创建包含表、业务规则和流程的 scoped app
  2. 02以版本控制方式部署元数据变更
  3. 03部署后查询实例以验证变更

MCP 相关

MCP REST 是该技能三层策略中的一层,用于查询或修改实时数据、管理 update sets 和运行后台脚本;也用于验证 Fluent 部署结果。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/happy-technologies-llc/happy-platform-skills --skill fluent-sdk

安装后在 Claude Code 中输入/fluent-sdk确认已加载。

附带文件vendor/now-sdk-explain.mdvendor/now-sdk-setup.md

详细介绍

使用 NowSDK Fluent 开发 ServiceNow

概述

此技能采用 三层开发策略,针对每项操作选择能力最强的工具:

TIER 1: NowSDK Fluent (metadata-as-code)
  Flows, tables, business rules, catalog items, ACLs, scoped apps
  TypeScript → build → deploy as scoped application
  ↓ falls through when...
  - Runtime data operations needed
  - Live instance queries required
  - Existing record manipulation

TIER 2: MCP REST Tools (runtime operations)
  CRUD on live data, queries, update set management, background scripts
  Direct REST API calls to instance
  ↓ falls through when...
  - REST API has documented limitations
  - UI-only operations (Flow compilation, UI Policy action linking)
  - Complex server-side operations not exposed via REST

TIER 3: Fix Scripts (manual fallback)
  SN-Create-Fix-Script for operations neither tier can handle
  Generated scripts for manual execution in instance
  Used for: Flow compilation, UI Policy setValue(), complex GlideRecord ops

适用场景:

  • “构建新的事件升级流程”
  • “创建包含表和业务规则的应用作用域”
  • “设置带审批工作流的目录项”
  • “使用版本控制部署元数据更改”
  • “我需要一个流程,但 REST 无法创建流程”

基础:ServiceNow 官方 Fluent 技能

此技能基于 ServiceNow 在 github.com/ServiceNow/sdk 提供的官方 AI 技能:

  • vendor/now-sdk-setup.md — 环境设置(Node 20+、SDK 安装、身份验证)。如果 now-sdk 命令失败,请先运行此技能。
  • vendor/now-sdk-explain.md — 通过 npx @servicenow/sdk explain 获取自我说明的 SDK 参考。编写 Fluent 代码前务必查阅此文档,其中涵盖所有元数据类型、约定和项目结构细节。

编写任何 Fluent 代码前,请遵循 explain 技能的流程:

  1. npx @servicenow/sdk explain <search-term> --list --format=raw — 查找相关主题
  2. npx @servicenow/sdk explain <topic> --peek --format=raw — 提交前预览
  3. npx @servicenow/sdk explain <topic> --format=raw — 阅读完整主题

这样可以确保使用最新的 API 签名和约定,而不是过时的示例。

前提条件

  • Node.js: v20+(node --version)
  • NowSDK: v4.6.0+(npm install -g @servicenow/sdk@latest)
  • 实例: 已启用 ServiceNow SDK 插件,用户拥有 admin 或 sn_sdk.user 角色
  • MCP 服务: 已配置并连接 happy-platform-mcp
  • 身份验证: 已针对目标实例完成 now-sdk auth
  • 相关技能: MCP 工具参考见 development/mcp-server,Fluent API 文档见 vendor/now-sdk-explain.md

层级决策矩阵

使用此矩阵确定每项操作由哪一层处理:

操作第 1 层(Fluent)第 2 层(MCP REST)第 3 层(修复脚本)
创建包含逻辑的流程是 — 完整的 Flow DSL否 — 无法创建流程否
编译/发布流程部署时自动完成否是 — 在 UI 中手动操作
创建表和架构是 — 使用 Table() 和类型化列是 — 但无版本控制否
业务规则是 — 使用 BusinessRule() 和脚本是 — 在 sys_script 上使用 SN-Create-Record否
目录项是 — 使用 CatalogItem() 和变量是 — 但 UI 策略有限部分支持
UI 策略操作是 — 随应用部署否 — REST 无法关联操作是 — setValue() 脚本
ACL是 — Acl() 定义是 — 在 sys_security_acl 上使用 SN-Create-Record否
脚本包含是 — 使用服务端模块是 — 在 sys_script_include 上使用 SN-Create-Record否
查询实时数据否 — 不适用于运行时是 — SN-Query-Table否
更新现有记录否 — 创建新的元数据是 — SN-Update-Record否
更新集管理部署时自动完成是 — SN-Set-Update-Set、检查、移动否
后台脚本执行否是 — SN-Execute-Background-Script否
创建应用作用域是 — now.config.json 定义作用域是 — 在 sys_app 上使用 SN-Create-Record否
部署到实例是 — now-sdk deploy否 — 逐条创建记录否
复杂 GlideRecord 操作否部分支持是 — 完整的服务端 API

流程

第 1 步:环境设置

按照 vendor/now-sdk-setup.md 验证 NowSDK 已安装并正常运行:

# Check Node version (must be 20+)
node --version

# Check SDK version (must be 4.6.0+)
npx @servicenow/sdk --version

# If not installed or outdated:
npm install -g @servicenow/sdk@latest

# Authenticate to your instance
now-sdk auth
# Follow prompts: instance URL, credentials

使用 explain 命令验证:

npx @servicenow/sdk explain quickstart --list --format=raw

然后熟悉即将使用的 Fluent API:

# Example: about to create flows
npx @servicenow/sdk explain Flow --list --peek --format=raw
npx @servicenow/sdk explain Flow --format=raw
步骤 2:确定合适的层级

编写任何代码之前,先对请求进行分类:

以下情况使用第 1 层(Fluent):

  • 从头创建新的元数据(流程、表、业务规则、目录项)
  • 构建范围应用
  • 需要版本控制、可重复的部署
  • Flow Designer 操作(REST API 无法创建流程)
  • 包含变量集、定价、访问控制的复杂目录项

以下情况使用第 2 层(MCP REST):

  • 查询或修改实时数据
  • 管理更新集(检查、移动、克隆)
  • 对现有记录进行一次性更新
  • 运行后台脚本
  • 验证第 1 层部署

以下情况使用第 3 层(修复脚本):

  • 流编译(部署后必须在 UI 中完成)
  • 通过 setValue() 链接 UI Policy 操作
  • REST 未提供支持的复杂 GlideRecord 操作
  • 需要服务器端性能的批量数据操作
步骤 3:搭建 Fluent 项目(第 1 层)

为新的范围应用创建项目结构。

项目结构:

my-sn-app/
├── now.config.json          # Scope and instance config
├── package.json             # Dependencies
├── tsconfig.json            # TypeScript config (optional)
├── src/
│   └── fluent/
│       ├── generated/
│       │   └── keys.ts      # Auto-generated sys_id mappings
│       ├── tables.now.ts    # Table definitions
│       ├── rules.now.ts     # Business rules
│       ├── flows.now.ts     # Flow definitions
│       ├── catalog.now.ts   # Catalog items
│       ├── acls.now.ts      # Access controls
│       └── *.server.js      # External script files
└── src/
    └── server/              # Server-side TypeScript modules

now.config.json:

{
  "scope": "x_myapp",
  "scopeId": "<sys_id from instance or generated on first deploy>"
}

package.json:

{
  "name": "my-sn-app",
  "version": "1.0.0",
  "scripts": {
    "build": "now-sdk build",
    "deploy": "now-sdk deploy",
    "explain": "now-sdk explain"
  },
  "devDependencies": {
    "@servicenow/glide": "catalog:",
    "@servicenow/sdk": "catalog:",
    "typescript": "catalog:"
  }
}
步骤 4:编写 Fluent 定义
表
// src/fluent/tables.now.ts
import {
  Table, StringColumn, IntegerColumn, BooleanColumn,
  DateColumn, ReferenceColumn
} from '@servicenow/sdk/core'

export const x_myapp_request = Table({
  name: 'x_myapp_request',
  schema: {
    title: StringColumn({ mandatory: true, label: 'Title', maxLength: 200 }),
    priority: IntegerColumn({ mandatory: true, label: 'Priority' }),
    active: BooleanColumn({ mandatory: true, label: 'Active', default: true }),
    due_date: DateColumn({ label: 'Due Date' }),
    assigned_to: ReferenceColumn({
      label: 'Assigned To',
      referenceTable: 'sys_user',
    }),
  },
})
业务规则
// src/fluent/rules.now.ts
import { BusinessRule } from '@servicenow/sdk/core'

export const validatePriority = BusinessRule({
  $id: Now.ID['validate_priority'],
  name: 'Validate Priority on Create',
  active: true,
  table: 'x_myapp_request',
  when: 'before',
  insert: true,
  script: Now.include('./validate-priority.server.js'),
})
// src/fluent/validate-priority.server.js
(function executeRule(current, previous) {
  if (!current.priority || current.priority < 1 || current.priority > 5) {
    current.priority = 3; // Default to medium
  }
  if (!current.assigned_to && current.priority == 1) {
    gs.addErrorMessage('Critical requests must have an assignee');
    current.setAbortAction(true);
  }
})(current, previous);
流
// src/fluent/flows.now.ts
import { action, Flow, wfa, trigger } from '@servicenow/sdk/automation'

export const escalationFlow = Flow(
  {
    $id: Now.ID['request_escalation_flow'],
    name: 'Request Escalation Flow',
    description: 'Escalates high-priority requests when unassigned after creation',
  },
  wfa.trigger(
    trigger.record.created,
    { $id: Now.ID['new_request_trigger'] },
    {
      table: 'x_myapp_request',
      condition: 'priority=1^assigned_toISEMPTY',
      run_flow_in: 'background',
      run_on_extended: 'false',
      run_when_setting: 'both',
      run_when_user_setting: 'any',
      run_when_user_list: [],
    }
  ),
  (params) => {
    // Log the escalation
    wfa.action(
      action.core.log,
      { $id: Now.ID['log_escalation'] },
      {
        log_level: 'warn',
        log_message: `Escalating unassigned critical request: ${wfa.dataPill(params.trigger.current.title, 'string')}`,
      }
    )

    // Look up the on-call manager
    const manager = wfa.action(
      action.core.lookUpRecord,
      { $id: Now.ID['lookup_oncall_manager'] },
      {
        table: 'sys_user',
        conditions: 'active=true^roles=manager',
        sort_type: 'sort_asc',
        if_multiple_records_are_found_action: 'use_first_record',
      }
    )

    // Auto-assign to manager
    wfa.action(
      action.core.updateRecord,
      { $id: Now.ID['assign_to_manager'] },
      {
        table_name: 'x_myapp_request',
        record: wfa.dataPill(params.trigger.current.sys_id, 'reference'),
        values: TemplateValue({
          assigned_to: wfa.dataPill(manager.Record.sys_id, 'reference'),
        }),
      }
    )

    // Send notification
    wfa.action(
      action.core.sendEmail,
      { $id: Now.ID['notify_manager'] },
      {
        table_name: 'x_myapp_request',
        watermark_email: true,
        ah_subject: `Critical Request Escalation: ${wfa.dataPill(params.trigger.current.title, 'string')}`,
        ah_body: `A critical request has been auto-assigned to you.`,
        record: wfa.dataPill(params.trigger.current.sys_id, 'reference'),
        ah_to: wfa.dataPill(manager.Record.email, 'string'),
      }
    )
  }
)
目录项
// src/fluent/catalog.now.ts
import {
  CatalogItem, VariableSet, SingleLineTextVariable,
  MultiLineTextVariable, SelectBoxVariable, ReferenceVariable,
  RequestedForVariable
} from '@servicenow/sdk/core'

const requestInfoVarSet = VariableSet({
  $id: Now.ID['request_info_varset'],
  title: 'Request Details',
  description: 'Core information for the request',
  internalName: 'request_info_varset',
  type: 'singleRow',
  layout: 'normal',
  order: 100,
  displayTitle: true,
  version: 1,
  variables: {
    requestTitle: SingleLineTextVariable({
      question: 'Request Title',
      mandatory: true,
      order: 100,
    }),
    requestDescription: MultiLineTextVariable({
      question: 'Description',
      mandatory: true,
      order: 200,
    }),
    requestPriority: SelectBoxVariable({
      question: 'Priority',
      mandatory: true,
      order: 300,
      choices: {
        critical: { label: '1 - Critical' },
        high: { label: '2 - High' },
        medium: { label: '3 - Medium' },
        low: { label: '4 - Low' },
      },
      defaultValue: 'medium',
    }),
  },
  name: 'Request Details',
})

export const newRequestCatalogItem = CatalogItem({
  $id: Now.ID['new_request_catalog_item'],
  name: 'New Service Request',
  shortDescription: 'Submit a new service request',
  description: 'Use this form to submit a service request. Critical requests trigger automatic escalation.',
  catalogs: ['e0d08b13c3330100c8b837659bba8fb4'], // Service Catalog
  categories: ['d258b953c611227a0146101fb1be7c31'], // Hardware (or your category)
  variableSets: [{ variableSet: requestInfoVarSet, order: 100 }],
  executionPlan: '523da512c611228900811a37c97c2014',
  variables: {
    requestedFor: RequestedForVariable({
      order: 1,
      question: 'Requested For',
    }),
    assignmentGroup: ReferenceVariable({
      order: 2,
      question: 'Assignment Group',
      referenceTable: 'sys_user_group',
    }),
  },
})
步骤 5:构建并部署(第 1 层)
# Build the project (validates TypeScript, generates metadata)
now-sdk build

# Deploy to instance (creates/updates scoped app and all metadata)
now-sdk deploy
步骤 6:使用 MCP 工具验证(第 2 层)

Fluent 部署后,使用 MCP 工具验证所有内容是否正确部署。

Tool: SN-Query-Table
Parameters:
  table_name: sys_update_xml
  query: application=<app_sys_id>
  fields: sys_id,type,name,sys_created_on
  limit: 50
Tool: SN-Get-Table-Schema
Parameters:
  table_name: x_myapp_request
Tool: SN-Query-Table
Parameters:
  table_name: sys_script
  query: collection=x_myapp_request^active=true
  fields: name,when,active
步骤 7:处理回退到修复脚本的情况(第 3 层)

对于 Fluent 和 REST 都无法处理的操作,请生成修复脚本。

流编译(部署后):

Tool: SN-Create-Fix-Script
Parameters:
  name: "Compile Escalation Flow"
  description: "Compile the request escalation flow after Fluent deployment"
  script: |
    // Navigate to Flow Designer and compile:
    // 1. Open Flow Designer
    // 2. Find "Request Escalation Flow"
    // 3. Click "Activate" to compile and enable
    //
    // This step cannot be automated - Flow Designer compilation
    // requires the UI runtime environment.
    gs.info('Manual step: Compile flow in Flow Designer UI');

UI Policy 操作链接(如有需要):

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var gr = new GlideRecord('sys_ui_policy_action');
    gr.addQuery('ui_policy', '<ui_policy_sys_id>');
    gr.addQuery('catalog_variable', '');
    gr.query();
    while (gr.next()) {
      gr.catalog_variable = 'IO:<variable_sys_id>';
      gr.update();
    }
  description: "Link UI Policy actions to catalog variables"
  execution_method: trigger

批量数据操作:

Tool: SN-Create-Fix-Script
Parameters:
  name: "Seed Sample Data"
  description: "Create initial sample records for testing"
  script: |
    var records = [
      { title: 'Test Request 1', priority: 1 },
      { title: 'Test Request 2', priority: 3 },
      { title: 'Test Request 3', priority: 5 }
    ];
    records.forEach(function(data) {
      var gr = new GlideRecord('x_myapp_request');
      gr.initialize();
      gr.title = data.title;
      gr.priority = data.priority;
      gr.active = true;
      gr.insert();
    });
    gs.info('Created ' + records.length + ' sample records');

混合工作流示例

结合全部三个层级的完整端到端示例:

GOAL: Build a scoped app with escalation flow, catalog item, and seed data

TIER 1 (Fluent):
  1. Create now.config.json with scope
  2. Define table schema in tables.now.ts
  3. Define business rules in rules.now.ts
  4. Define escalation flow in flows.now.ts
  5. Define catalog item in catalog.now.ts
  6. now-sdk build && now-sdk deploy

TIER 2 (MCP REST):
  7. SN-Query-Table → verify all records in update set
  8. SN-Get-Table-Schema → confirm table columns created
  9. SN-Query-Table on sys_script → verify business rules active
  10. SN-Execute-Background-Script → seed sample data

TIER 3 (Fix Script):
  11. SN-Create-Fix-Script → flow compilation instructions
  12. SN-Create-Fix-Script → any UI Policy action linking
  13. Manual: compile flow in Flow Designer UI

NowSDK 说明参考

SDK 提供可通过 CLI 访问的内置文档:

# List all available topics
npx @servicenow/sdk explain --list --format=raw

# Search for a topic
npx @servicenow/sdk explain flow --list --peek --format=raw

# Read full topic (only after previewing)
npx @servicenow/sdk explain flow --format=raw

# Key topics:
#   quickstart  - Getting started
#   Table       - Table definitions
#   Flow        - Flow automation
#   BusinessRule - Business rule definitions
#   CatalogItem - Service catalog items
#   Acl         - Access control lists
#   ScriptInclude - Script includes
#   naming      - Naming conventions
#   structure   - Project structure

**重要提示:**务必先使用 --peek,再阅读完整主题,以免将上下文浪费在无关内容上。

Fluent API 快速参考

按模块导入
// Core metadata
import {
  Table, StringColumn, IntegerColumn, BooleanColumn,
  DateColumn, ReferenceColumn, Record, BusinessRule,
  ScriptInclude, ClientScript, Acl, Role
} from '@servicenow/sdk/core'

// Flow automation
import {
  Flow, Subflow, action, wfa, trigger
} from '@servicenow/sdk/automation'

// Catalog
import {
  CatalogItem, VariableSet, SingleLineTextVariable,
  MultiLineTextVariable, SelectBoxVariable, ReferenceVariable,
  CheckBoxVariable, DateTimeVariable, RequestedForVariable,
  EmailVariable
} from '@servicenow/sdk/core'
关键辅助函数
辅助函数用途示例
Now.ID['key']按键名引用记录$id: Now.ID['my_rule']
Now.include('./file.js')引入外部脚本文件script: Now.include('./rule.server.js')
Now.ref(export)交叉引用其他 Fluent 导出项table: Now.ref(myTable)
Now.attach('./file')将文件附加到记录attachment: Now.attach('./logo.png')
TemplateValue({})设置流操作中的字段值values: TemplateValue({ state: '2' })
wfa.dataPill(ref, type)引用流数据小圆点wfa.dataPill(params.trigger.current.sys_id, 'reference')
流逻辑块
// Conditional
wfa.flowLogic.if({ $id, condition, annotation }, () => { ... })
wfa.flowLogic.elseIf({ $id, condition, annotation }, () => { ... })
wfa.flowLogic.else({ $id }, () => { ... })

// Loops
wfa.flowLogic.forEach(dataPill, { $id }, () => { ... })

// Subflow outputs
wfa.flowLogic.assignSubflowOutputs({ $id }, params.outputs, { ... })
流操作(action.core.*)
操作用途
action.core.log按级别记录消息
action.core.lookUpRecord查询单条记录
action.core.updateRecord更新记录
action.core.createRecord创建新记录
action.core.sendEmail发送电子邮件通知
action.core.sendSms发送短信
action.core.sendNotification通过模板发送通知
流触发器(trigger.record.*)
触发器触发条件
trigger.record.created创建记录时
trigger.record.updated更新记录时
trigger.record.createdOrUpdated创建或更新记录时
trigger.record.deleted删除记录时

最佳实践

  • 编写 Fluent 元数据前,请查阅 vendor/now-sdk-explain.md。
  • 将生成的元数据保存在源代码管理中,并审查平台上的相应变更。
  • 使用层级决策矩阵,避免在没有明确需要的情况下混用部署模式。

故障排除

未找到 NowSDK
npm install -g @servicenow/sdk@latest
# Verify: npx @servicenow/sdk --version (must be 4.6.0+)
身份验证失败
# Re-authenticate
now-sdk auth
# Ensure instance has SDK plugin enabled
# Ensure user has admin or sn_sdk.user role
构建错误
# Check explain docs for the specific type
npx @servicenow/sdk explain <TypeName> --format=raw

# Common issues:
# - Missing $id on records → add Now.ID['unique_key']
# - Invalid column types → check Table column type imports
# - Script file not found → verify Now.include() path is relative to .now.ts file
部署创建了错误的范围

请确认 now.config.json 中的 scope 和 scopeId 正确。scopeId 应与实例中 sys_app 记录的 sys_id 匹配。

回退:Fluent 失败时

如果 now-sdk deploy 无法处理特定记录类型,请按以下步骤回退:

  1. 检查 MCP REST 是否可以直接创建该记录(SN-Create-Record)
  2. 如果 REST 也失败,则生成修复脚本(SN-Create-Fix-Script)
  3. 记录此限制,以供日后参考

评论

支持 Markdown