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. 04构建 Scripted REST API
  5. 05准备应用发布与版本管理

适用场景

  1. 01为业务功能创建独立应用
  2. 02控制应用对其他作用域的访问
  3. 03配置显式依赖和应用属性
  4. 04准备应用商店发布

MCP 相关

指南展示了通过 MCP 的 snow_create_application 创建 ServiceNow 应用的示例。

安装

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

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

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

详细介绍

ServiceNow 的限定范围应用开发

限定范围应用可为 ServiceNow 中的自定义开发提供隔离性和可移植性。

为什么使用限定范围应用?

特性全局范围限定范围应用
命名冲突可能发生可避免(x_prefix)
可移植性困难简单(更新集)
安全性开放受控(跨范围)
商店发布不支持支持
依赖项隐式显式

创建限定范围应用

通过 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

定义脚本化 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_ids在其他实例上失败使用属性或查找
缺少 ACL安全漏洞为所有表创建 ACL
没有错误处理静默失败添加 try/catch 和日志记录
直接访问全局表升级冲突使用引用,而非副本

最佳实践

  1. 单一职责 - 每个业务功能使用一个应用
  2. 显式依赖项 - 声明所有要求
  3. 属性驱动 - 无需更改代码即可配置
  4. 防御式编码 - 操作前检查访问权限
  5. 文档 - 包含 README 和发行说明
  6. 测试 - 为关键功能编写自动化测试
  7. 版本控制 - 更新时采用语义化版本

评论

支持 Markdown