mcpField
S
scoped-apps

scoped-apps

github.com/serac-labs開発7756 回インストール9 週間前に更新
リポジトリ

scoped-apps について

scoped-appsは、ServiceNowで分離性と移植性を備えたアプリを開発するためのガイドです。アプリ作成、スコープ間アクセス、REST API、依存関係、リリース手順を扱い、MCPによる作成例も掲載しています。

主な機能

  1. 01スコープ付きアプリの作成
  2. 02スコープ間アクセスの管理
  3. 03プロパティと依存関係の設定
  4. 04Scripted REST APIの構築
  5. 05リリースとバージョン管理の準備

活用シーン

  1. 01業務機能ごとの独立アプリの作成
  2. 02アプリスコープ間のアクセス制御
  3. 03依存関係とプロパティの明示的な設定
  4. 04Store公開に向けた準備

MCP 対応

snow_create_applicationを使ってMCP経由でServiceNowアプリを作成する例を示しています。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/serac-labs/serac --skill scoped-apps

インストール後、Claude Code で/scoped-appsを入力して読み込まれたか確認します。

詳細

ServiceNow 向けスコープ付きアプリケーション開発

スコープ付きアプリケーションは、ServiceNow のカスタム開発に分離性と移植性をもたらします。

スコープ付きアプリを使用する理由

機能グローバルスコープスコープ付きアプリ
名前の競合発生する可能性あり防止される(x_prefix)
移植性困難簡単(Update Sets)
セキュリティ開放制御される(スコープ間)
ストアへの公開いいえはい
依存関係暗黙的明示的

スコープ付きアプリケーションの作成

Studio から作成(推奨)
1. Navigate: System Applications > Studio
2. Click: Create Application
3. Enter:
   - Name: "My Custom App"
   - Scope: "x_mycom_myapp" (auto-generated)
   - Version: 1.0.0
4. Configure:
   - Runtime access: Check tables needing cross-scope access
MCP 経由で作成
snow_create_application({
  name: "My Custom Application",
  scope: "x_mycom_custom",
  version: "1.0.0",
  description: "Custom application for...",
})

スコープの命名規則

x_[vendor]_[app]

Examples:
- x_acme_hr          (ACME Corp HR App)
- x_mycom_inventory  (My Company Inventory)
- x_snc_global       (ServiceNow Global)

テーブルの命名

// Scoped tables are automatically prefixed
// Table name in Studio: "task_tracker"
// Actual table name: "x_mycom_myapp_task_tracker"

// Creating records
var gr = new GlideRecord("x_mycom_myapp_task_tracker")
gr.initialize()
gr.setValue("name", "My Task")
gr.insert()

スコープ付きアプリ内の Script Include

var TaskManager = Class.create()
TaskManager.prototype = {
  initialize: function () {
    this.tableName = "x_mycom_myapp_task_tracker"
  },

  createTask: function (name, description) {
    var gr = new GlideRecord(this.tableName)
    gr.initialize()
    gr.setValue("name", name)
    gr.setValue("description", description)
    return gr.insert()
  },

  // Mark as accessible from other scopes
  // Requires: "Accessible from: All application scopes"
  getTask: function (sysId) {
    var gr = new GlideRecord(this.tableName)
    if (gr.get(sysId)) {
      return {
        name: gr.getValue("name"),
        description: gr.getValue("description"),
      }
    }
    return null
  },

  type: "TaskManager",
}

スコープ間アクセス

別のスコープの Script Include の呼び出し
// From scope: x_mycom_otherapp
// Calling: x_mycom_myapp.TaskManager

// Option 1: Direct call (if accessible)
var tm = new x_mycom_myapp.TaskManager()
var task = tm.getTask(sysId)

// Option 2: GlideScopedEvaluator
var evaluator = new GlideScopedEvaluator()
evaluator.putVariable("sysId", sysId)
var result = evaluator.evaluateScript("x_mycom_myapp", "new TaskManager().getTask(sysId)")
別のスコープのテーブルへのアクセス
// Check if cross-scope access is allowed
var gr = new GlideRecord("x_other_app_table")
if (!gr.isValid()) {
  gs.error("No access to x_other_app_table")
  return
}

// If accessible, query normally
gr.addQuery("active", true)
gr.query()

アプリケーションプロパティ

プロパティの定義
// In Application > Properties
// Name: x_mycom_myapp.default_priority
// Value: 3
// Type: string

// In Application > Modules
// Create "Properties" module pointing to:
// /sys_properties_list.do?sysparm_query=name=x_mycom_myapp
プロパティの使用
// Get property value
var defaultPriority = gs.getProperty("x_mycom_myapp.default_priority", "3")

// Set property value (requires admin)
gs.setProperty("x_mycom_myapp.default_priority", "2")

アプリケーションファイルの構成

x_mycom_myapp/
├── Tables
│   ├── x_mycom_myapp_task
│   └── x_mycom_myapp_config
├── Script Includes
│   ├── TaskManager
│   └── ConfigUtils
├── Business Rules
│   └── Validate Task
├── UI Pages
│   └── task_dashboard
├── REST API
│   └── Task API
├── Scheduled Jobs
│   └── Daily Cleanup
└── Application Properties
    ├── default_priority
    └── enable_notifications

スコープ付きアプリ内の REST API

Scripted REST API の定義
// Resource: /api/x_mycom_myapp/tasks
// HTTP Method: GET

;(function process(request, response) {
  var tasks = []
  var gr = new GlideRecord("x_mycom_myapp_task_tracker")
  gr.addQuery("active", true)
  gr.query()

  while (gr.next()) {
    tasks.push({
      sys_id: gr.getUniqueValue(),
      name: gr.getValue("name"),
      status: gr.getValue("status"),
    })
  }

  response.setBody({
    result: tasks,
    count: tasks.length,
  })
})(request, response)
API の呼び出し
curl -X GET \
  "https://instance.service-now.com/api/x_mycom_myapp/tasks" \
  -H "Authorization: Bearer token"

アプリケーションの依存関係

依存関係の宣言
Application > Dependencies
Add:
  - sn_hr_core (HR Core)
  - sn_cmdb (CMDB)
コード内での依存関係の確認
// Check if plugin is active
if (GlidePluginManager.isActive("com.snc.hr.core")) {
  // HR Core is available
  var hrCase = new sn_hr_core.hr_case()
}

ストアへの公開

公開前のチェックリスト
□ All tables have proper ACLs
□ No hard-coded sys_ids
□ No hard-coded instance URLs
□ All dependencies declared
□ Properties have default values
□ Documentation complete
□ Test cases pass
□ No global scope modifications
□ Update Set tested on clean instance
バージョン管理
Major.Minor.Patch
1.0.0 - Initial release
1.1.0 - New feature added
1.1.1 - Bug fix
2.0.0 - Breaking change

よくある間違い

間違い問題解決策
グローバルスコープの変更正しくデプロイできない変更をスコープ内に収める
sys_id のハードコード他のインスタンスで失敗するプロパティまたは検索を使用する
ACL の不足セキュリティ上の脆弱性すべてのテーブルに ACL を作成する
エラー処理がないエラーが通知されずに失敗するtry/catch とログ記録を追加する
グローバルテーブルへの直接アクセスアップグレード時の競合コピーではなく参照を使用する

ベストプラクティス

  1. 単一責任 - ビジネス機能ごとにアプリを 1 つ作成します
  2. 依存関係の明示 - 必要な依存関係をすべて宣言します
  3. プロパティ駆動 - コードを変更せずに設定できるようにします
  4. 防御的なコーディング - 操作の前にアクセス権を確認します
  5. ドキュメント - README とリリースノートを含めます
  6. テスト - 重要な機能に自動テストを用意します
  7. バージョン管理 - 更新にはセマンティックバージョニングを使用します

コメント