fluent-sdk について
NowSDK Fluent、MCP REST、Fix Scriptを使い分けるServiceNow開発スキルです。ServiceNowアプリの開発者向けに、メタデータ、実行時データ、他の方法で扱えない処理を適切な手段に振り分け、バージョン管理に基づくデプロイを説明します。
主な機能
- 01Fluent、MCP REST、Fix Scriptへの処理の振り分け
- 02TypeScriptによるアプリメタデータの定義
- 03スコープアプリのビルドとデプロイ
- 04MCP経由での実行時レコードの照会と更新
- 05フローのコンパイルなどをFix Scriptで処理
活用シーン
- 01テーブル、ビジネスルール、フローを含むスコープアプリの作成
- 02メタデータ変更のバージョン管理とデプロイ
- 03デプロイ後にインスタンスを照会して変更を検証
MCP 対応
MCP RESTは、実行時データの照会・変更、Update Setの管理、バックグラウンドスクリプトの実行を担う手段の一つです。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 開発
概要
このスキルは、ServiceNow 向けの3 層開発戦略を実装し、各操作を最も適したツールに振り分けます。
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 スキル
このスキルは、github.com/ServiceNow/sdk の ServiceNow 公式 AI スキルを基盤としています。
vendor/now-sdk-setup.md— 環境の設定(Node 20 以降、SDK のインストール、認証)。now-sdkコマンドが失敗する場合は、まずこれを実行してください。vendor/now-sdk-explain.md—npx @servicenow/sdk explainを使用した、自己文書化型の SDK リファレンス。Fluent コードを書く前に必ず参照してください。すべてのメタデータ型、規約、プロジェクト構造の詳細が記載されています。
Fluent コードを書く前に、explain スキルの手順に従ってください。
npx @servicenow/sdk explain <search-term> --list --format=raw— 関連するトピックを探しますnpx @servicenow/sdk explain <topic> --peek --format=raw— 採用する前に内容を確認します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
層の判断マトリックス
各操作をどの層で処理するか判断するには、このマトリックスを使用してください。
| 操作 | Tier 1(Fluent) | Tier 2(MCP REST) | Tier 3(Fix Script) |
|---|---|---|---|
| ロジックを含むフローの作成 | はい - 完全な 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(Fix Script)を選択する場合:
- フローのコンパイル(デプロイ後に UI で実行する必要があります)
setValue()による UI ポリシーアクションのリンク- 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:Fix Script へのフォールスルーに対応する(ティア 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 ポリシーアクションのリンク(必要な場合):
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');
ハイブリッドワークフローの例
3 つのティアすべてを組み合わせた、エンドツーエンドの完全な例です。
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 Explain リファレンス
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 | SMS を送信する |
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 が失敗した場合は、次の手順に進みます。
- MCP REST でレコードを直接作成できるか確認します(
SN-Create-Record) - REST でも失敗する場合は、修正スクリプトを生成します(
SN-Create-Fix-Script) - 今後の参照用に制限事項を記録します