关于 scoped-apps
scoped-apps 是面向 ServiceNow 开发者的作用域应用开发指南,介绍隔离、依赖、安全控制和发布流程。内容覆盖应用创建、跨作用域访问、REST API 与版本管理,并给出 MCP 创建示例。
主要功能
- 01创建作用域应用
- 02管理跨作用域访问
- 03配置应用属性与依赖
- 04构建 Scripted REST API
- 05准备应用发布与版本管理
适用场景
- 01为业务功能创建独立应用
- 02控制应用对其他作用域的访问
- 03配置显式依赖和应用属性
- 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 和日志记录 |
| 直接访问全局表 | 升级冲突 | 使用引用,而非副本 |
最佳实践
- 单一职责 - 每个业务功能使用一个应用
- 显式依赖项 - 声明所有要求
- 属性驱动 - 无需更改代码即可配置
- 防御式编码 - 操作前检查访问权限
- 文档 - 包含 README 和发行说明
- 测试 - 为关键功能编写自动化测试
- 版本控制 - 更新时采用语义化版本
评论
支持 Markdown