aitable

package
v1.0.63 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 3, 2026 License: Apache-2.0 Imports: 36 Imported by: 0

Documentation

Overview

Package aitable provides declarative shortcuts for the DingTalk AI 表格 (aitable) service: Base / table / field / record / view / form / dashboard / chart / workflow / advanced-permission / section management. Each shortcut maps 1:1 onto an MCP tool declared in internal/helpers/aitable.go.

Tool routing follows the public aitable snapshot.

Copyright 2026 Alibaba Group SPDX-License-Identifier: Apache-2.0

Copyright 2026 Alibaba Group SPDX-License-Identifier: Apache-2.0

Copyright 2026 Alibaba Group SPDX-License-Identifier: Apache-2.0

Copyright 2026 Alibaba Group SPDX-License-Identifier: Apache-2.0

Copyright 2026 Alibaba Group SPDX-License-Identifier: Apache-2.0

Copyright 2026 Alibaba Group SPDX-License-Identifier: Apache-2.0

Copyright 2026 Alibaba Group SPDX-License-Identifier: Apache-2.0

Copyright 2026 Alibaba Group SPDX-License-Identifier: Apache-2.0

Copyright 2026 Alibaba Group SPDX-License-Identifier: Apache-2.0

Copyright 2026 Alibaba Group SPDX-License-Identifier: Apache-2.0

Copyright 2026 Alibaba Group SPDX-License-Identifier: Apache-2.0

Copyright 2026 Alibaba Group SPDX-License-Identifier: Apache-2.0

Copyright 2026 Alibaba Group SPDX-License-Identifier: Apache-2.0

Copyright 2026 Alibaba Group SPDX-License-Identifier: Apache-2.0

Copyright 2026 Alibaba Group SPDX-License-Identifier: Apache-2.0

Index

Constants

This section is empty.

Variables

View Source
var AdvpermDisable = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+advperm-disable",
	Product:     serverHelper,
	Description: "关闭指定 Base 的高级权限总开关(所有自定义角色失效)",
	Intent:      "当你要关闭某 Base 的高级权限总开关时使用;会使该 Base 下所有自定义角色失效、影响成员访问权限,请谨慎确认。",
	Risk:        shortcut.RiskHighWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
	},
	Tips: []string{`dws aitable +advperm-disable --base-id B`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("set_advanced_permission", map[string]any{
			"baseId":  rt.Str("base-id"),
			"enabled": false,
		})
	},
}

AdvpermDisable 关闭高级权限总开关(set_advanced_permission, enabled=false)。

View Source
var AdvpermEnable = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+advperm-enable",
	Product:     serverHelper,
	Description: "开启指定 Base 的高级权限总开关",
	Intent:      "当你要为某 Base 打开高级权限总开关、以便后续配置自定义角色和精细权限时使用;会实际开启该 Base 的高级权限。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
	},
	Tips: []string{`dws aitable +advperm-enable --base-id B`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("set_advanced_permission", map[string]any{
			"baseId":  rt.Str("base-id"),
			"enabled": true,
		})
	},
}

AdvpermEnable 开启高级权限总开关(set_advanced_permission, enabled=true)。

View Source
var AttachmentDownload = shortcut.Shortcut{Service: "aitable", Command: "+record-download-attachment", Product: serverMain, Description: "按记录单元格中的准确 resourceId 下载附件,校验字节数并返回 SHA256", Intent: "已有 Base/表/记录/附件字段和 resourceId 时;先重新读取该附件的签名地址,完整下载后才发布,不覆盖本地已有文件。", Risk: shortcut.RiskRead, Safety: contract.SafetySpec{Effect: "read", Risk: "low", Confirmation: "not_required", Idempotency: "idempotent"}, Contract: aitableCompositeContractWithResult("+record-download-attachment", "按准确附件 ID 下载并验证字节数", "下载已知记录附件并交付真实本地文件时", "只有孤立 fileToken 不能定位所属记录;上传用 +attachment-put;移除用 +attachment-remove", `dws aitable +record-download-attachment --base-id B --table-id T --record-id R --field-id F --resource-id A --output attachment.bin`, &contract.ResultSpec{Outcomes: []contract.ResultOutcome{contract.ResultOutcomeSuccess, contract.ResultOutcomeFailure}, DataSchema: json.RawMessage(`{"type":"object","properties":{"resourceId":{"type":"string","description":"实际下载的附件资源 ID"},"output":{"type":"string","description":"工作目录内相对路径"},"sizeBytes":{"type":"integer","description":"经校验的实际字节数"},"sha256":{"type":"string","description":"实际下载字节的 SHA256"}},"required":["resourceId","output","sizeBytes","sha256"]}`)}), Flags: []shortcut.Flag{{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true}, {Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true}, {Name: "record-id", Type: shortcut.FlagString, Desc: "准确记录 ID", Required: true}, {Name: "field-id", Type: shortcut.FlagString, Desc: "附件字段 ID", Required: true}, {Name: "resource-id", Type: shortcut.FlagString, Desc: "同一附件单元格中的准确 resourceId", Required: true}, {Name: "output", Type: shortcut.FlagString, Desc: "工作目录内相对路径,不覆盖已有文件", Required: true}}, Execute: func(rt *shortcut.RuntimeContext) error {
	cwd, err := aitableWorkingDirectory()
	if err != nil {
		return err
	}
	if _, _, err = localio.ResolveOutputPath(cwd, rt.Str("output"), "", "attachment.bin"); err != nil {
		return err
	}
	_, items, err := readAttachmentCell(rt, rt.Str("base-id"), rt.Str("table-id"), rt.Str("record-id"), rt.Str("field-id"))
	if err != nil {
		return err
	}
	item, err := exactAttachmentForDownload(items, rt.Str("resource-id"))
	if err != nil {
		return err
	}
	size, ok := numericInt64(item["size"])
	if !ok || size < 0 {
		return fmt.Errorf("attachment lacks valid byte size")
	}
	url := stringValue(item, "url")
	if _, err = localio.ValidateDownloadURL(url); err != nil {
		return fmt.Errorf("attachment lacks a usable HTTPS download URL")
	}
	if rt.DryRun() {
		return rt.Output(map[string]any{"executed": false, "resourceId": rt.Str("resource-id"), "sizeBytes": size})
	}
	f, err := downloadAITableAttachment(rt.Command().Context(), url, localio.DownloadOptions{BaseDir: cwd, Output: rt.Str("output"), PreferredName: attachmentName(item), ExpectedSize: &size})
	if err != nil {
		return err
	}
	return rt.Output(map[string]any{"resourceId": rt.Str("resource-id"), "output": f.RelativePath, "sizeBytes": f.SizeBytes, "sha256": f.SHA256})
}}
View Source
var AttachmentPut = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+attachment-put",
	Product:     serverMain,
	Description: "准备凭证、实际 PUT 本地文件、写入 attachment 单元格并读回验证",
	Intent:      "当你要把本地文件真正上传并落到指定记录附件字段时使用;不是只返回 uploadUrl,支持 replace 或在现有项可安全重写时 append。",
	Risk:        shortcut.RiskWrite,
	Safety: contract.SafetySpec{
		Effect: "write", Risk: "medium", Confirmation: "user_required", Idempotency: "non_idempotent",
	},
	Contract: aitableCompositeContract(
		"+attachment-put",
		"准备凭证、实际 PUT 本地文件、写入 attachment 单元格并读回验证",
		"当你要把本地文件真正上传并落到指定记录附件字段时使用;不是只返回 uploadUrl,支持 replace 或在现有项可安全重写时 append。",
		"只申请上传凭证用 attachment upload;不要用 drive fileId;现有附件读回缺 fileToken 时无法安全 append,只能 replace 或停止",
		`dws aitable +attachment-put --base-id B --table-id T --record-id R --field-id F --file ./report.pdf --mode replace`,
	),
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "record-id", Type: shortcut.FlagString, Desc: "Record ID", Required: true},
		{Name: "field-id", Type: shortcut.FlagString, Desc: "attachment Field ID", Required: true},
		{Name: "file", Type: shortcut.FlagString, Desc: "第一个本地文件路径", Required: true},
		{Name: "additional-files", Type: shortcut.FlagStringSlice, Desc: "同次上传的其他文件,最多合计 10 个;名称须不同,全部上传后一次写入单元格"},
		{Name: "mode", Type: shortcut.FlagString, Default: "append", Desc: "append 保留现有附件;replace 整体替换", Enum: []string{"append", "replace"}},
		{Name: "mime-type", Type: shortcut.FlagString, Desc: "覆盖自动推断的 MIME type(可选)"},
	},
	Tips: []string{`dws aitable +attachment-put --base-id B --table-id T --record-id R --field-id F --file ./report.pdf --mode replace`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return executeAttachmentPut(rt)
	},
}
View Source
var AttachmentRemove = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+attachment-remove",
	Product:     serverMain,
	Description: "清空 attachment 字段或按文件名解析 resourceId 删除,并读回验证",
	Intent:      "清空附件或按精确文件名移除时使用;优先按真实 resourceId 删除,回读验证目标移除与其余附件保留,存在服务端并发覆盖窗口。",
	Risk:        shortcut.RiskHighWrite,
	Safety: contract.SafetySpec{
		Effect: "write", Risk: "high", Confirmation: "user_required", Idempotency: "idempotent",
	},
	Contract: aitableCompositeContract(
		"+attachment-remove",
		"清空 attachment 字段或按文件名解析 resourceId 删除,并读回验证",
		"清空附件或按精确文件名移除时使用;优先按真实 resourceId 删除,回读验证目标移除与其余附件保留,存在服务端并发覆盖窗口。",
		"已知 resourceId 时可直接用 attachment remove;目标缺 resourceId 时仅在剩余项都有 fileToken 的情况下使用替换路径,否则停止",
		`dws aitable +attachment-remove --base-id B --table-id T --record-id R --field-id F --clear-all`,
	),
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "record-id", Type: shortcut.FlagString, Desc: "Record ID", Required: true},
		{Name: "field-id", Type: shortcut.FlagString, Desc: "attachment Field ID", Required: true},
		{Name: "remove-name", Type: shortcut.FlagString, Desc: "移除精确文件名的所有匹配项;与 --resource-ids 或 --clear-all=true 三选一"},
		{Name: "resource-ids", Type: shortcut.FlagStringSlice, Desc: "精确 resourceId 列表;与非空 remove-name 或 clear-all=true 互斥"},
		{Name: "clear-all", Type: shortcut.FlagBool, Desc: "true 时清空该字段全部附件;与 --remove-name 或 --resource-ids 三选一;false 不选择清空"},
	},

	Tips: []string{`dws aitable +attachment-remove --base-id B --table-id T --record-id R --field-id F --clear-all`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return executeAttachmentRemove(rt)
	},
}
View Source
var AttachmentUpload = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+attachment-upload",
	Product:     serverMain,
	Description: "为 attachment 字段申请 OSS 直传地址(uploadUrl / fileToken)",
	Intent:      "当你要往 attachment(附件)字段上传文件、需要先申请 OSS 直传地址时使用;返回 uploadUrl 和 fileToken,供你直传文件后再写入记录。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "file-name", Type: shortcut.FlagString, Desc: "文件名(含扩展名)", Required: true},
		{Name: "size", Type: shortcut.FlagInt, Desc: "文件大小(字节),须 > 0", Required: true},
		{Name: "mime-type", Type: shortcut.FlagString, Desc: "MIME type,如 image/png(可选)"},
	},
	Tips: []string{`dws aitable +attachment-upload --base-id B --file-name report.xlsx --size 204800`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":   rt.Str("base-id"),
			"fileName": rt.Str("file-name"),
			"size":     rt.Int("size"),
		}
		if rt.Changed("mime-type") {
			params["mimeType"] = rt.Str("mime-type")
		}
		return rt.CallMCP("prepare_attachment_upload", params)
	},
}

AttachmentUpload 准备附件上传(prepare_attachment_upload)。

View Source
var BaseBootstrap = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+base-bootstrap",
	Product:     serverMain,
	Description: "一次创建 Base、数据表和字段,逐层读回验证并在中断时报告已知副作用",
	Intent:      "当你已有声明式 tables JSON、想一次搭好一套 AI 表格结构时使用;表内字段自动按 15 个拆批,每次创建都读回验证。",
	Risk:        shortcut.RiskWrite,
	Safety: contract.SafetySpec{
		Effect: "write", Risk: "medium", Confirmation: "user_required", Idempotency: "non_idempotent",
	},
	Contract: aitableCompositeContract(
		"+base-bootstrap",
		"一次创建 Base、数据表和字段,逐层读回验证并在中断时报告已知副作用",
		"当你已有声明式 tables JSON、想一次搭好一套 AI 表格结构时使用;表内字段自动按 15 个拆批,每次创建都读回验证。",
		"已有 Base 只需新增一张表时用 +table-bootstrap;复制现有 Base 用 +base-copy;不要对失败请求盲目重试",
		`dws aitable +base-bootstrap --name "项目管理" --tables '[{"name":"任务","fields":[]}]'`,
	),
	Flags: []shortcut.Flag{
		{Name: "name", Type: shortcut.FlagString, Desc: "新 Base 名称", Required: true},
		{Name: "folder-id", Type: shortcut.FlagString, Desc: "目标知识库文件夹 ID(可选)"},
		{Name: "template-id", Type: shortcut.FlagString, Desc: "模板 ID(可选)"},
		{Name: "tables", Type: shortcut.FlagString, Desc: `表结构 JSON 数组:[{'name':'任务','fields':[...]}]`, Required: true},
	},
	Tips: []string{`dws aitable +base-bootstrap --name "项目管理" --tables '[{"name":"任务","fields":[]}]'`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return executeBaseBootstrap(rt)
	},
}
View Source
var BaseCopy = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+base-copy",
	Product:     serverMain,
	Description: "复制 AI 表格(可选目标目录,可仅复制结构)",
	Intent:      "当你想基于现有表格快速复刻一份(如做模板或备份)时使用;未指定目标时复制到源 Base 所在工作区根目录。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "源 Base ID 或标准 Base 节点 URL", Required: true},
		{Name: "target-folder-id", Type: shortcut.FlagString, Desc: "可选目标文件夹 dentryUuid、标准节点 URL 或 Drive 文件夹 URL;不传时使用源 Base 工作区根目录"},
		{Name: "only-struct", Type: shortcut.FlagBool, Desc: "仅复制结构(不含数据),默认 false"},
		{Name: "new-name", Type: shortcut.FlagString, Desc: "复制后设置的新 Base 名称(1-50 个字符)"},
	},
	Tips: []string{
		`dws aitable +base-copy --base-id BASE_ID --new-name "副本名称"`,
		`dws aitable +base-copy --base-id BASE_URL --target-folder-id FOLDER_URL --only-struct`,
	},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return executeBaseCopy(rt)
	},
}

BaseCopy 复制 AI 表格(copy_base)。

View Source
var BaseDelete = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+base-delete",
	Product:     serverMain,
	Description: "删除指定 Base(不可逆)",
	Intent:      "当你确认要彻底删除某个 AI 表格时使用;会不可逆地删除整个 Base 及其所有数据表和记录,操作前务必核对 baseId。",
	Risk:        shortcut.RiskHighWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "待删除 Base ID", Required: true},
		{Name: "reason", Type: shortcut.FlagString, Desc: "删除原因(可选)"},
	},
	Tips: []string{`dws aitable +base-delete --base-id BASE_ID`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{"baseId": rt.Str("base-id"), "confirm": true}
		if rt.Changed("reason") {
			params["reason"] = rt.Str("reason")
		}
		return rt.CallMCP("delete_base", params)
	},
}

BaseDelete 删除 AI 表格(delete_base)。

View Source
var BaseGet = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+base-get",
	Product:     serverMain,
	Description: "获取指定 Base 的目录信息(tables / dashboards summary)",
	Intent:      "当你已有 baseId、需要了解这个表格里有哪些数据表和仪表盘(拿到 tableId/dashboardId)以便进一步操作时使用;返回 Base 的目录结构概要。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_base_get",
			CanonicalPath:  "aitable.shortcut_base_get",
			CLIPath:        "aitable +base-get",
			PrimaryCLIPath: "aitable +base-get",
		},
		Description: "获取指定 Base 的目录信息(tables / dashboards summary)",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "获取指定 Base 的目录信息(tables / dashboards summary)",
			UseWhen:      []string{"当你已有 baseId、需要了解这个表格里有哪些数据表和仪表盘(拿到 tableId/dashboardId)以便进一步操作时使用;返回 Base 的目录结构概要。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples:     []string{"dws aitable +base-get --base-id BASE_ID"},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
	},
	Tips: []string{`dws aitable +base-get --base-id BASE_ID`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("get_base", map[string]any{"baseId": rt.Str("base-id")})
	},
}

BaseGet 获取 AI 表格信息(get_base)。

View Source
var BaseGetPrimaryDocID = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+base-get-primary-doc-id",
	Product:     serverMain,
	Description: "根据 baseId/tableId/recordId 查询主键文档是否存在及其 dentryUuid",
	Intent:      "当某条记录的主键列是文档类型、你需要确认主键文档是否已创建或拿到其 dentryUuid 时使用;返回 exists,已创建时同时返回 nodeId,未创建时可改用 +record-primary-doc-create。",
	Risk:        shortcut.RiskRead,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "record-id", Type: shortcut.FlagString, Desc: "记录 ID", Required: true},
	},
	Tips: []string{`dws aitable +base-get-primary-doc-id --base-id B --table-id T --record-id R`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return callCompatibleAITableRead(rt, "get_cell_doc", "get_base_primary_doc_id", map[string]any{
			"baseId":   rt.Str("base-id"),
			"tableId":  rt.Str("table-id"),
			"recordId": rt.Str("record-id"),
		})
	},
}

BaseGetPrimaryDocID 获取记录主键文档 ID(get_cell_doc)。

View Source
var BaseList = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+base-list",
	Product:     serverMain,
	Description: "获取当前用户可访问的 AI 表格 Base 列表(最近访问,支持游标分页)",
	Intent:      "当你不知道具体 baseId、想先浏览自己最近用过或可访问的 AI 表格清单以便定位目标时使用;支持游标分页,返回 Base 列表及其 baseId。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_base_list",
			CanonicalPath:  "aitable.shortcut_base_list",
			CLIPath:        "aitable +base-list",
			PrimaryCLIPath: "aitable +base-list",
		},
		Description: "获取当前用户可访问的 AI 表格 Base 列表(最近访问,支持游标分页)",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "获取当前用户可访问的 AI 表格 Base 列表(最近访问,支持游标分页)",
			UseWhen:      []string{"当你不知道具体 baseId、想先浏览自己最近用过或可访问的 AI 表格清单以便定位目标时使用;支持游标分页,返回 Base 列表及其 baseId。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples: []string{
				"dws aitable +base-list",
				"dws aitable +base-list --limit 5 --cursor NEXT",
			},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "limit", Type: shortcut.FlagInt, Desc: "每页数量,默认 10,最大 10"},
		{Name: "cursor", Type: shortcut.FlagString, Desc: "分页游标,首次不传"},
	},
	Tips: []string{`dws aitable +base-list`, `dws aitable +base-list --limit 5 --cursor NEXT`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{}
		if rt.Changed("limit") {
			params["limit"] = rt.Int("limit")
		}
		if rt.Changed("cursor") {
			params["cursor"] = rt.Str("cursor")
		}
		data, err := rt.CallMCPData(serverMain, "list_bases", params)
		if err != nil {
			return err
		}
		bases, err := baseListProject("list_bases", data)
		if err != nil {
			return err
		}
		return outputBasePage(rt, bases, data)
	},
}

BaseList 获取 AI 表格列表(list_bases)。

View Source
var BaseSchemaSnapshot = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+base-schema-snapshot",
	Product:     serverMain,
	Description: "读取 Base、全部数据表、字段和视图的可复用结构快照,并严格校验每层响应",
	Intent:      "当你要审计、迁移或复制一个 Base 的完整结构但不需要记录数据时使用;明确空 tables/fields/views 合法,缺失容器或缺少请求 ID 则失败。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low", Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: aitableCompositeContract(
		"+base-schema-snapshot",
		"读取 Base、全部数据表、字段和视图的可复用结构快照,并严格校验每层响应",
		"当你要审计、迁移或复制一个 Base 的完整结构但不需要记录数据时使用;明确空 tables/fields/views 合法,缺失容器或缺少请求 ID 则失败。",
		"需要记录数据用 record query;只看 Base 目录用 base get;快照不会创建或修改资源",
		"dws aitable +base-schema-snapshot --base-id BASE_ID",
	),
	Flags: []shortcut.Flag{{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true}},
	Tips:  []string{`dws aitable +base-schema-snapshot --base-id BASE_ID`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return executeBaseSchemaSnapshot(rt)
	},
}
View Source
var BaseSearch = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+base-search",
	Product:     serverMain,
	Description: "按名称关键词搜索 AI 表格 Base",
	Intent:      "当你知道某个 AI 表格的名字或部分关键词、想直接定位到它并拿到 baseId 时使用;输入名称关键词,返回匹配的 Base 列表。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_base_search",
			CanonicalPath:  "aitable.shortcut_base_search",
			CLIPath:        "aitable +base-search",
			PrimaryCLIPath: "aitable +base-search",
		},
		Description: "按名称关键词搜索 AI 表格 Base",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "按名称关键词搜索 AI 表格 Base",
			UseWhen:      []string{"当你知道某个 AI 表格的名字或部分关键词、想直接定位到它并拿到 baseId 时使用;输入名称关键词,返回匹配的 Base 列表。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples:     []string{"dws aitable +base-search --query \"项目管理\""},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "query", Type: shortcut.FlagString, Desc: "Base 名称关键词", Required: true},
		{Name: "cursor", Type: shortcut.FlagString, Desc: "分页游标,首次不传"},
	},
	Tips: []string{`dws aitable +base-search --query "项目管理"`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{"query": rt.Str("query")}
		if rt.Changed("cursor") {
			params["cursor"] = rt.Str("cursor")
		}
		data, err := rt.CallMCPData(serverMain, "search_bases", params)
		if err != nil {
			return err
		}
		bases, err := baseListProject("search_bases", data)
		if err != nil {
			return err
		}
		return outputBasePage(rt, bases, data)
	},
}

BaseSearch 按名称关键词搜索 AI 表格(search_bases)。

View Source
var BaseUpdate = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+base-update",
	Product:     serverMain,
	Description: "更新 Base 名称(可选备注)",
	Intent:      "当你要给已有 AI 表格改名或修改备注说明时使用;会实际修改指定 Base 的名称/描述。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "name", Type: shortcut.FlagString, Desc: "新名称,1-50 字符", Required: true},
		{Name: "desc", Type: shortcut.FlagString, Desc: "备注文本(可选)"},
	},
	Tips: []string{`dws aitable +base-update --base-id BASE_ID --name "新名称"`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":      rt.Str("base-id"),
			"newBaseName": rt.Str("name"),
		}
		if rt.Changed("desc") {
			params["description"] = rt.Str("desc")
		}
		return rt.CallMCP("update_base", params)
	},
}

BaseCreate 创建 AI 表格(create_base)。 BaseUpdate 更新 AI 表格(update_base)。

View Source
var ChartDelete = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+chart-delete",
	Product:     serverMain,
	Description: "删除指定 chart 及其布局项(不可逆)",
	Intent:      "当你确认要删除某个图表时使用;会连同其在仪表盘上的布局项一并移除,不可逆。",
	Risk:        shortcut.RiskHighWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "dashboard-id", Type: shortcut.FlagString, Desc: "Dashboard ID", Required: true},
		{Name: "chart-id", Type: shortcut.FlagString, Desc: "Chart ID", Required: true},
		{Name: "reason", Type: shortcut.FlagString, Desc: "删除原因(可选)"},
	},
	Tips: []string{`dws aitable +chart-delete --base-id B --dashboard-id D --chart-id C`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":      rt.Str("base-id"),
			"dashboardId": rt.Str("dashboard-id"),
			"chartId":     rt.Str("chart-id"),
			"confirm":     true,
		}
		if rt.Changed("reason") {
			params["reason"] = rt.Str("reason")
		}
		return rt.CallMCP("delete_chart", params)
	},
}

ChartDelete 删除图表(delete_chart)。

View Source
var ChartGet = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+chart-get",
	Product:     serverMain,
	Description: "获取指定 chart 的详细信息",
	Intent:      "当你要查看某个图表的配置详情(统计维度、样式等)时使用;返回指定 chart 的详细信息。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_chart_get",
			CanonicalPath:  "aitable.shortcut_chart_get",
			CLIPath:        "aitable +chart-get",
			PrimaryCLIPath: "aitable +chart-get",
		},
		Description: "获取指定 chart 的详细信息",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "获取指定 chart 的详细信息",
			UseWhen:      []string{"当你要查看某个图表的配置详情(统计维度、样式等)时使用;返回指定 chart 的详细信息。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples:     []string{"dws aitable +chart-get --base-id B --dashboard-id D --chart-id C"},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "dashboard-id", Type: shortcut.FlagString, Desc: "Dashboard ID", Required: true},
		{Name: "chart-id", Type: shortcut.FlagString, Desc: "Chart ID", Required: true},
	},
	Tips: []string{`dws aitable +chart-get --base-id B --dashboard-id D --chart-id C`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("get_chart", map[string]any{
			"baseId":      rt.Str("base-id"),
			"dashboardId": rt.Str("dashboard-id"),
			"chartId":     rt.Str("chart-id"),
		})
	},
}

ChartGet 获取图表信息(get_chart)。

View Source
var ChartShareGet = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+chart-share-get",
	Product:     serverMain,
	Description: "查询 chart 的分享配置",
	Intent:      "当你要查看某图表是否已开启对外分享及其分享方式时使用;返回 chart 的分享配置。",
	Risk:        shortcut.RiskRead,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "dashboard-id", Type: shortcut.FlagString, Desc: "Dashboard ID", Required: true},
		{Name: "chart-id", Type: shortcut.FlagString, Desc: "Chart ID", Required: true},
	},
	Tips: []string{`dws aitable +chart-share-get --base-id B --dashboard-id D --chart-id C`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("get_chart_share", map[string]any{
			"baseId":      rt.Str("base-id"),
			"dashboardId": rt.Str("dashboard-id"),
			"chartId":     rt.Str("chart-id"),
		})
	},
}

ChartShareGet 获取图表分享配置(get_chart_share)。

View Source
var ChartShareUpdate = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+chart-share-update",
	Product:     serverMain,
	Description: "开启/关闭 chart 分享并可设置分享类型",
	Intent:      "当你要对外开启或关闭单个图表的分享(可选公开 PUBLIC 或仅组织内 ORG)时使用;会实际改变 chart 的分享设置。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "dashboard-id", Type: shortcut.FlagString, Desc: "Dashboard ID", Required: true},
		{Name: "chart-id", Type: shortcut.FlagString, Desc: "Chart ID", Required: true},
		{Name: "enabled", Type: shortcut.FlagBool, Desc: "是否开启分享", Required: true},
		{Name: "share-type", Type: shortcut.FlagString, Desc: "分享类型(仅开启时生效)", Enum: []string{"PUBLIC", "ORG"}},
		{Name: "allow-back-to-doc", Type: shortcut.FlagBool, Desc: "是否允许回到文档(可选)"},
	},
	Tips: []string{`dws aitable +chart-share-update --base-id B --dashboard-id D --chart-id C --enabled true --share-type ORG`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":      rt.Str("base-id"),
			"dashboardId": rt.Str("dashboard-id"),
			"chartId":     rt.Str("chart-id"),
			"enabled":     rt.Bool("enabled"),
		}
		if rt.Changed("share-type") {
			params["shareType"] = rt.Str("share-type")
		}
		if rt.Changed("allow-back-to-doc") {
			params["allowBackToDoc"] = rt.Bool("allow-back-to-doc")
		}
		return rt.CallMCP("update_chart_share", params)
	},
}

ChartShareUpdate 更新图表分享配置(update_chart_share)。

View Source
var ChartUpdate = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+chart-update",
	Product:     serverMain,
	Description: "更新指定 chart 的配置或布局(--config 必填;layout 写前强制校验 12/48 列协议)",
	Intent:      "当你要修改某图表的配置(如改名、换统计维度)或调整其在仪表盘上的布局时使用;传 layout 时会先读取 Dashboard 元信息并校验根网格。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "dashboard-id", Type: shortcut.FlagString, Desc: "Dashboard ID", Required: true},
		{Name: "chart-id", Type: shortcut.FlagString, Desc: "Chart ID", Required: true},
		{Name: "config", Type: shortcut.FlagString, Desc: "图表配置 JSON(至少含 chartName)", Required: true},
		{Name: "layout", Type: shortcut.FlagString, Desc: "布局 JSON(可选)"},
		{Name: "is-app-mode", Type: shortcut.FlagBool, Desc: "只读应用模式上下文;仅已确认应用模式且更新 layout 时传 true,不写入 MCP payload"},
	},
	Tips: []string{`dws aitable +chart-update --base-id B --dashboard-id D --chart-id C --config '{"chartName":"柱图"}'`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		cfg, err := parseJSONObject("config", rt.Str("config"))
		if err != nil {
			return err
		}
		if err := aitableprotocol.ValidateDashboardPersistentMetadata("config", cfg); err != nil {
			return err
		}
		params := map[string]any{
			"baseId":      rt.Str("base-id"),
			"dashboardId": rt.Str("dashboard-id"),
			"chartId":     rt.Str("chart-id"),
			"config":      cfg,
		}
		if rt.Changed("layout") {
			layout, err := parseJSONObject("layout", rt.Str("layout"))
			if err != nil {
				return err
			}
			if err := aitableprotocol.ValidateDashboardPersistentMetadata("layout", layout); err != nil {
				return err
			}
			dashboard, err := rt.CallMCPData(serverMain, "get_dashboard", map[string]any{
				"baseId":      rt.Str("base-id"),
				"dashboardId": rt.Str("dashboard-id"),
			})
			if err != nil {
				return err
			}
			totalColumns, err := aitableprotocol.ResolveDashboardRootColumns(
				dashboard,
				rt.Str("base-id"),
				rt.Str("dashboard-id"),
				rt.Changed("is-app-mode") && rt.Bool("is-app-mode"))
			if err != nil {
				return apperrors.NewAPI(
					fmt.Sprintf("Chart 布局写前验证失败:%v;未执行 update_chart", err),
					apperrors.WithOperation("aitable/get_dashboard"),
					apperrors.WithOrigin("mcp"),
					apperrors.WithFailureStage("response_validation"),
					apperrors.WithExecutionStarted(false),
					apperrors.WithRetryable(false),
					apperrors.WithReason("dashboard_protocol_evidence_invalid"),
				)
			}
			if err := aitableprotocol.ValidateRootChartLayout(layout, totalColumns); err != nil {
				return apperrors.NewValidation(
					fmt.Sprintf("Chart 布局写前验证失败:%v;未执行 update_chart", err),
					apperrors.WithReason("invalid_chart_layout"),
					apperrors.WithFailureStage("request_validation"),
					apperrors.WithExecutionStarted(false),
					apperrors.WithRetryable(false),
				)
			}
			params["layout"] = layout
		}
		return rt.CallMCP("update_chart", params)
	},
}

ChartCreate 创建图表(create_chart)。 ChartUpdate 更新图表(update_chart)。

View Source
var ChartWidgetsExample = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+chart-widgets-example",
	Product:     serverMain,
	Description: "获取所有图表类型的 widget config 示例",
	Intent:      "当你准备创建或修改图表、需要先参考各类图表 widget config 的示例结构时使用;返回所有图表类型的配置示例。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_chart_widgets_example",
			CanonicalPath:  "aitable.shortcut_chart_widgets_example",
			CLIPath:        "aitable +chart-widgets-example",
			PrimaryCLIPath: "aitable +chart-widgets-example",
		},
		Description: "获取所有图表类型的 widget config 示例",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "获取所有图表类型的 widget config 示例",
			UseWhen:      []string{"当你准备创建或修改图表、需要先参考各类图表 widget config 的示例结构时使用;返回所有图表类型的配置示例。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples:     []string{"dws aitable +chart-widgets-example"},
		},
	},
	Tips: []string{`dws aitable +chart-widgets-example`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("get_dashboard_widgets_example", map[string]any{})
	},
}

ChartWidgetsExample 获取图表配置示例(get_dashboard_widgets_example)。

View Source
var DashboardArrange = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+dashboard-arrange",
	Product:     serverHelper,
	Description: "对指定仪表盘做服务端智能布局重排",
	Intent:      "当仪表盘里的图表排布凌乱、你想让系统自动重新排版对齐时使用;会实际调整该仪表盘的图表布局。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "dashboard-id", Type: shortcut.FlagString, Desc: "Dashboard ID", Required: true},
	},
	Tips: []string{`dws aitable +dashboard-arrange --base-id B --dashboard-id D`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("align_dashboard", map[string]any{
			"baseId":      rt.Str("base-id"),
			"dashboardId": rt.Str("dashboard-id"),
		})
	},
}

DashboardArrange 自动重排仪表盘图表布局(align_dashboard,server: aitable)。

View Source
var DashboardConfigExample = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+dashboard-config-example",
	Product:     serverMain,
	Description: "获取 dashboard config 的结构示例",
	Intent:      "当你准备创建或更新仪表盘、需要先了解 dashboard config 的字段结构长什么样时使用;返回一份配置结构示例供参考。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_dashboard_config_example",
			CanonicalPath:  "aitable.shortcut_dashboard_config_example",
			CLIPath:        "aitable +dashboard-config-example",
			PrimaryCLIPath: "aitable +dashboard-config-example",
		},
		Description: "获取 dashboard config 的结构示例",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "获取 dashboard config 的结构示例",
			UseWhen:      []string{"当你准备创建或更新仪表盘、需要先了解 dashboard config 的字段结构长什么样时使用;返回一份配置结构示例供参考。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples:     []string{"dws aitable +dashboard-config-example"},
		},
	},
	Tips: []string{`dws aitable +dashboard-config-example`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("get_dashboard_config_example", map[string]any{})
	},
}

DashboardConfigExample 获取仪表盘配置示例(get_dashboard_config_example)。

View Source
var DashboardDelete = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+dashboard-delete",
	Product:     serverMain,
	Description: "删除指定 dashboard(级联删除其 chart,不可逆)",
	Intent:      "当你确认要删除某个仪表盘时使用;会级联删除其下所有图表,不可逆。",
	Risk:        shortcut.RiskHighWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "dashboard-id", Type: shortcut.FlagString, Desc: "Dashboard ID", Required: true},
		{Name: "reason", Type: shortcut.FlagString, Desc: "删除原因(可选)"},
	},
	Tips: []string{`dws aitable +dashboard-delete --base-id B --dashboard-id D`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":      rt.Str("base-id"),
			"dashboardId": rt.Str("dashboard-id"),
			"confirm":     true,
		}
		if rt.Changed("reason") {
			params["reason"] = rt.Str("reason")
		}
		return rt.CallMCP("delete_dashboard", params)
	},
}

DashboardDelete 删除仪表盘(delete_dashboard)。

View Source
var DashboardGet = shortcut.Shortcut{

	OutputRollout: output.RolloutDualValidate,
	Service:       "aitable",
	Command:       "+dashboard-get",
	Product:       serverMain,
	Description:   "获取指定 dashboard 的详细信息(含只读 schemaVersion 类型证据与 charts summary)",
	Intent:        "当你要查看某仪表盘的配置详情、规划 12/48 列根布局或获取 chartId 时使用;原样保留 meta.schemaVersion 与 schemaVersionTypeVerified。",
	Risk:          shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_dashboard_get",
			CanonicalPath:  "aitable.shortcut_dashboard_get",
			CLIPath:        "aitable +dashboard-get",
			PrimaryCLIPath: "aitable +dashboard-get",
		},
		Description: "获取指定 dashboard 的详细信息(含只读 schemaVersion 类型证据与 charts summary)",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "获取指定 dashboard 的详细信息(含只读 schemaVersion 类型证据与 charts summary)",
			UseWhen:      []string{"当你要查看某仪表盘的配置详情、规划 12/48 列根布局或获取 chartId 时使用;原样保留 meta.schemaVersion 与 schemaVersionTypeVerified。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples:     []string{"dws aitable +dashboard-get --base-id B --dashboard-id D"},
		},
		Result: dashboardGetResultSpec(),
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "dashboard-id", Type: shortcut.FlagString, Desc: "Dashboard ID", Required: true},
	},
	Tips: []string{`dws aitable +dashboard-get --base-id B --dashboard-id D`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("get_dashboard", map[string]any{
			"baseId":      rt.Str("base-id"),
			"dashboardId": rt.Str("dashboard-id"),
		})
	},
}

DashboardGet 获取仪表盘信息(get_dashboard)。

View Source
var DashboardList = shortcut.Shortcut{Service: "aitable", Command: "+dashboard-list", Product: serverMain, Description: "从完整 Base 目录读取仪表盘列表并验证每项 ID", Intent: "要发现 Base 中全部 dashboardId 时使用;原始 Base 目录使用 +base-get。", Risk: shortcut.RiskRead, Safety: contract.SafetySpec{Effect: "read", Risk: "low", Confirmation: "not_required", Idempotency: "idempotent"}, Contract: aitableCompositeContractWithResult("+dashboard-list", "列出 Base 中全部仪表盘", "发现 Base 内仪表盘 ID 时", "已知 dashboardId 用 +dashboard-get", `dws aitable +dashboard-list --base-id B`, &contract.ResultSpec{Outcomes: []contract.ResultOutcome{contract.ResultOutcomeSuccess, contract.ResultOutcomeFailure}, DataSchema: json.RawMessage(`{"type":"object","properties":{"baseId":{"type":"string","description":"所属 Base"},"count":{"type":"integer","description":"仪表盘数量"},"dashboards":{"type":"array","description":"服务端目录中的仪表盘","items":{"type":"object","properties":{"dashboardId":{"type":"string","description":"稳定 ID"}},"required":["dashboardId"]}}},"required":["baseId","count","dashboards"]}`)}), Flags: []shortcut.Flag{{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true}}, Execute: func(rt *shortcut.RuntimeContext) error {
	r, err := rt.CallMCPData(serverMain, "get_base", map[string]any{"baseId": rt.Str("base-id")})
	if err != nil {
		return err
	}
	body := parityResponseObject(r)
	if body["baseId"] != rt.Str("base-id") {
		return fmt.Errorf("get_base identity mismatch")
	}
	list, ok := body["dashboards"].([]any)
	if !ok {
		return fmt.Errorf("get_base lacks dashboards collection")
	}
	seen := map[string]bool{}
	for _, v := range list {
		m, ok := v.(map[string]any)
		id := stringValue(m, "dashboardId")
		if !ok || id == "" || seen[id] {
			return fmt.Errorf("dashboard collection contains invalid identity")
		}
		seen[id] = true
	}
	return rt.Output(map[string]any{"baseId": rt.Str("base-id"), "count": len(list), "dashboards": list})
}}
View Source
var DashboardShareGet = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+dashboard-share-get",
	Product:     serverMain,
	Description: "查询 dashboard 的分享配置",
	Intent:      "当你要查看某仪表盘是否已开启对外分享及其分享方式时使用;返回 dashboard 的分享配置。",
	Risk:        shortcut.RiskRead,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "dashboard-id", Type: shortcut.FlagString, Desc: "Dashboard ID", Required: true},
	},
	Tips: []string{`dws aitable +dashboard-share-get --base-id B --dashboard-id D`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("get_dashboard_share", map[string]any{
			"baseId":      rt.Str("base-id"),
			"dashboardId": rt.Str("dashboard-id"),
		})
	},
}

DashboardShareGet 获取仪表盘分享配置(get_dashboard_share)。

View Source
var DashboardShareUpdate = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+dashboard-share-update",
	Product:     serverMain,
	Description: "开启/关闭 dashboard 分享并可设置分享类型",
	Intent:      "当你要对外开启或关闭仪表盘分享(可选公开 PUBLIC 或仅组织内 ORG)时使用;会实际改变 dashboard 的分享设置。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "dashboard-id", Type: shortcut.FlagString, Desc: "Dashboard ID", Required: true},
		{Name: "enabled", Type: shortcut.FlagBool, Desc: "是否开启分享", Required: true},
		{Name: "share-type", Type: shortcut.FlagString, Desc: "分享类型(仅开启时生效)", Enum: []string{"PUBLIC", "ORG"}},
		{Name: "allow-back-to-doc", Type: shortcut.FlagBool, Desc: "是否允许回到文档(可选)"},
	},
	Tips: []string{`dws aitable +dashboard-share-update --base-id B --dashboard-id D --enabled true --share-type PUBLIC`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":      rt.Str("base-id"),
			"dashboardId": rt.Str("dashboard-id"),
			"enabled":     rt.Bool("enabled"),
		}
		if rt.Changed("share-type") {
			params["shareType"] = rt.Str("share-type")
		}
		if rt.Changed("allow-back-to-doc") {
			params["allowBackToDoc"] = rt.Bool("allow-back-to-doc")
		}
		return rt.CallMCP("update_dashboard_share", params)
	},
}

DashboardShareUpdate 更新仪表盘分享配置(update_dashboard_share)。

View Source
var DashboardUpdate = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+dashboard-update",
	Product:     serverMain,
	Description: "更新指定 dashboard 的配置",
	Intent:      "当你要修改仪表盘的名称或整体配置时使用;会实际更新指定 dashboard 的配置。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "dashboard-id", Type: shortcut.FlagString, Desc: "Dashboard ID", Required: true},
		{Name: "config", Type: shortcut.FlagString, Desc: "dashboard 配置 JSON(可选,与 --name 二选一)"},
		{Name: "name", Type: shortcut.FlagString, Desc: "dashboard 名称(可选,与 --config 二选一)"},
	},
	Tips: []string{`dws aitable +dashboard-update --base-id B --dashboard-id D --name "新名称"`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		cfg := map[string]any{}
		if rt.Changed("config") {
			m, err := parseJSONObject("config", rt.Str("config"))
			if err != nil {
				return err
			}
			cfg = m
		}
		if rt.Changed("name") {
			cfg["name"] = rt.Str("name")
		}
		if len(cfg) == 0 {
			return fmt.Errorf("必须指定 --config 或 --name")
		}
		return rt.CallMCP("update_dashboard", map[string]any{
			"baseId":      rt.Str("base-id"),
			"dashboardId": rt.Str("dashboard-id"),
			"config":      cfg,
		})
	},
}

DashboardCreate 创建仪表盘(create_dashboard)。 DashboardUpdate 更新仪表盘(update_dashboard)。

View Source
var DataQuery = shortcut.Shortcut{
	Service: "aitable", Command: "+data-query", Product: serverMain,
	Description: "用 DWS JSON DSL 统一执行标量或分组聚合,不拉全表做本地统计",
	Intent:      "明确单表统计任务时使用;dsl.stats 必填,dsl.group 非空时走分组聚合。使用 DWS fieldId/statsType 协议,不直接接受 Lark dimensions/measures。",
	Risk:        shortcut.RiskRead, Safety: contract.SafetySpec{Effect: "read", Risk: "low", Confirmation: "not_required", Idempotency: "idempotent"},
	Contract: aitableCompositeContractWithResult("+data-query", "用 DWS JSON DSL 统一执行标量或分组聚合", "需要对单表执行多个指标或分组统计时", "明细读取用 +record-query;多表 JOIN 或 SQL 用 aitable psql;Lark DSL 需显式转换", `dws aitable +data-query --base-id B --table-id T --dsl '{"stats":[{"fieldId":"fldAmount","statsType":"SUM"}]}'`, &contract.ResultSpec{Outcomes: []contract.ResultOutcome{contract.ResultOutcomeSuccess, contract.ResultOutcomeFailure}, DataSchema: json.RawMessage(`{"type":"object","properties":{"baseId":{"type":"string","description":"目标 Base"},"tableId":{"type":"string","description":"目标数据表"},"mode":{"type":"string","description":"scalar 或 grouped"},"result":{"type":"object","description":"原生聚合数据,保留统计值精度与版本信息"}},"required":["baseId","tableId","mode","result"]}`)}),
	Flags:    []shortcut.Flag{{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true}, {Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true}, {Name: "dsl", Type: shortcut.FlagString, Desc: "DWS DSL 对象:stats 为 1-20 项 fieldId/statsType;可选 group/filters/sort/dataVersion/keyword;不接受 limit 以免静默统计子集", Required: true}},
	Execute:  executeDataQuery,
}
View Source
var DatasourceCreate = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+datasource-create",
	Product:     serverMain,
	Description: "为指定 AI 表格创建数据源同步配置,创建一张数据源表并触发首次全量同步。返回新建数据源表 ID 和同步任务 ID。",
	Intent:      "当用户需要将外部数据源(如审批数据)接入 AI 表格时使用。创建后返回的表 ID 可用于后续同步、更新配置或查询状态。",
	Risk:        shortcut.RiskWrite,
	Safety: contract.SafetySpec{
		Effect: "write", Risk: "medium",
		Confirmation: "not_required", Idempotency: "non_idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_datasource_create",
			CanonicalPath:  "aitable.shortcut_datasource_create",
			CLIPath:        "aitable +datasource-create",
			PrimaryCLIPath: "aitable +datasource-create",
		},
		Description: "为指定 AI 表格创建数据源同步配置,创建一张数据源表并触发首次全量同步。返回新建数据源表 ID 和同步任务 ID。",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "为指定 AI 表格创建数据源同步配置,创建一张数据源表并触发首次全量同步。返回新建数据源表 ID 和同步任务 ID。",
			UseWhen:      []string{"当用户需要将外部数据源(如审批数据)接入 AI 表格时使用。创建后返回的表 ID 可用于后续同步、更新配置或查询状态。"},
			AvoidWhen: []string{
				"目标 Base 已有数据源表且仅需更新配置时(改用 +datasource-update)",
				"仅需触发已有数据源表的同步时(改用 +datasource-sync)",
			},
			Examples: []string{
				`dws aitable +datasource-create --base-id BASE123 --datasource-type OA --source-config '{"processCode":"PROC-XXXX","name":"采购申请","dataType":"recent_time","recentDays":"30d","iconUrl":"https://example.com/icon.png","url":"https://example.com/oa"}'`,
				`dws aitable +datasource-create --base-id BASE123 --datasource-type OA --source-config '{"processCode":"PROC-XXXX","name":"采购申请","dataType":"time_range","startDate":"2025-01-01","endDate":"2025-12-31","iconUrl":"https://example.com/icon.png","url":"https://example.com/oa"}' --auto`,
			},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "目标 Base ID(通过 +base-list / +base-search 获取)", Required: true},
		{Name: "datasource-type", Type: shortcut.FlagString, Desc: "数据源类型,目前支持审批(OA)", Required: true},
		{Name: "source-config", Type: shortcut.FlagString, Desc: "源配置 JSON 字符串。字段分为两类:须从 +datasource-list-sources 结果原样透传的字段(必填):processCode(审批流程编码)、name(展示名称)、iconUrl(图标 URL)、url(跳转链接);调用方自行设置的字段:dataType(必填,time_range/start_time/recent_time)、recentDays(dataType=recent_time 时有效,7d/30d/1y,默认 30d)、startDate(dataType=time_range/start_time 时有效,yyyy-MM-dd,默认 30 天前)、endDate(dataType=time_range 时有效,yyyy-MM-dd,默认当天)、keepRemovedFields(是否保留已删除字段,默认 false)。约定:syncAll 固定为 true;splitParentTableField 与 enableDataSyncOaDetailList 为下游内部字段,无需传入", Required: true},
		{Name: "auto", Type: shortcut.FlagBool, Desc: "是否开启自动同步,默认 false;创建新数据源表时该字段始终下发给下游"},
		{Name: "field-ids", Type: shortcut.FlagStringSlice, Desc: "不受支持:当前仅支持全量同步,请勿传入"},
		{Name: "auto-sync-setting", Type: shortcut.FlagString, Desc: "自动同步频率配置 JSON 字符串,仅在 --auto=true 时生效。字段:syncType(必填,hourly=按小时间隔,scheduled=定时触发)、hourlyInterval(syncType=hourly 时必填,正整数小时)、scheduleType(syncType=scheduled 时必填,daily/weekly/monthly)、timeValue(syncType=scheduled 时必填,HH:mm)、selectedMonthDays(scheduleType=monthly 时必填,每月几号触发,1-31)、selectedWeekdays(scheduleType=weekly 时必填,每周哪几天触发,1=周一…7=周日)、skipNonWorkingDay(可选,默认 false)。不传时使用下游默认自动同步策略"},
	},
	Tips: []string{
		`dws aitable +datasource-create --base-id BASE123 --datasource-type OA --source-config '{"processCode":"PROC-XXXX","name":"采购申请","dataType":"recent_time","recentDays":"30d","iconUrl":"https://example.com/icon.png","url":"https://example.com/oa"}'`,
		`dws aitable +datasource-create --base-id BASE123 --datasource-type OA --source-config '{"processCode":"PROC-XXXX","name":"采购申请","dataType":"time_range","startDate":"2025-01-01","endDate":"2025-12-31","iconUrl":"https://example.com/icon.png","url":"https://example.com/oa"}' --auto`,
	},
	Execute: func(rt *shortcut.RuntimeContext) error {
		if _, err := parseJSONObject("source-config", rt.Str("source-config")); err != nil {
			return err
		}
		params := map[string]any{
			"baseId":         rt.Str("base-id"),
			"datasourceType": rt.Str("datasource-type"),
			"sourceConfig":   rt.Str("source-config"),
		}
		params["auto"] = rt.Bool("auto")
		if rt.Changed("field-ids") {
			return apperrors.NewValidation("--field-ids 不受支持:当前数据源仅支持全量同步,请移除此参数")
		}
		if rt.Changed("auto-sync-setting") {
			v := rt.Str("auto-sync-setting")
			if v == "" {
				return fmt.Errorf("--auto-sync-setting 显式提供时不能为空,如需保持默认请勿传入")
			}
			if _, err := parseJSONObject("auto-sync-setting", v); err != nil {
				return err
			}
			params["autoSyncSetting"] = v
		}
		data, err := rt.CallMCPData(serverMain, "create_datasource", params)
		if err != nil {
			return err
		}
		return rt.Output(data)
	},
}

DatasourceCreate 为指定 AI 表格创建数据源同步配置(create_datasource)。

View Source
var DatasourceGetConfig = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+datasource-get-config",
	Product:     serverMain,
	Description: "获取指定数据源表的同步配置信息,包括源配置、是否全量同步、是否自动同步、同步状态等。仅适用于数据源表(sync=true),普通表会返回错误。仅支持 OA 审批数据源(datasourceType=OA),其他数据源类型暂不支持,待后续开放。返回的 sourceConfig 包含数据源连接信息(如审批模板 ID、源表 ID 等)。",
	Intent:      "当用户需要查看已有数据源表的配置详情(如确认当前同步的审批模板、字段结构、自动同步状态)时使用。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_datasource_get_config",
			CanonicalPath:  "aitable.shortcut_datasource_get_config",
			CLIPath:        "aitable +datasource-get-config",
			PrimaryCLIPath: "aitable +datasource-get-config",
		},
		Description: "获取指定数据源表的同步配置信息,包括源配置、是否全量同步、是否自动同步、同步状态等。仅适用于数据源表(sync=true),普通表会返回错误。仅支持 OA 审批数据源(datasourceType=OA),其他数据源类型暂不支持,待后续开放。返回的 sourceConfig 包含数据源连接信息(如审批模板 ID、源表 ID 等)。",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "获取指定数据源表的同步配置信息,包括源配置、是否全量同步、是否自动同步、同步状态等。仅适用于数据源表(sync=true),普通表会返回错误。仅支持 OA 审批数据源(datasourceType=OA),其他数据源类型暂不支持,待后续开放。返回的 sourceConfig 包含数据源连接信息(如审批模板 ID、源表 ID 等)。",
			UseWhen:      []string{"当用户需要查看已有数据源表的配置详情(如确认当前同步的审批模板、字段结构、自动同步状态)时使用。"},
			AvoidWhen: []string{
				"需要更新配置时(改用 +datasource-update)",
				"需要查询同步任务状态时(改用 +datasource-sync-status)",
			},
			Examples: []string{
				`dws aitable +datasource-get-config --base-id BASE123 --table-id TBL456`,
			},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "目标 Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "数据源表 ID(通过 +base-get / +table-list 获取,仅允许传入 sync=true 的表)", Required: true},
	},
	Tips: []string{
		`dws aitable +datasource-get-config --base-id BASE123 --table-id TBL456`,
	},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
		}
		data, err := rt.CallMCPData(serverMain, "get_datasource_config", params)
		if err != nil {
			return err
		}
		return rt.Output(data)
	},
}

DatasourceGetConfig 获取数据源表的同步配置信息(get_datasource_config)。

View Source
var DatasourceGetFields = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+datasource-get-fields",
	Product:     serverMain,
	Description: "获取指定数据源下可供同步的字段列表,用于了解来源字段结构;当前仅支持全量同步。传入从 +datasource-list-sources 获取的 sourceConfig。仅支持 OA 审批数据源(datasourceType=OA),其他数据源类型暂不支持,待后续开放。",
	Intent:      "当用户需要查看某数据源来源有哪些可同步字段、以便了解创建或更新数据源时的字段结构时使用。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_datasource_get_fields",
			CanonicalPath:  "aitable.shortcut_datasource_get_fields",
			CLIPath:        "aitable +datasource-get-fields",
			PrimaryCLIPath: "aitable +datasource-get-fields",
		},
		Description: "获取指定数据源下可供同步的字段列表,用于了解来源字段结构;当前仅支持全量同步。传入从 +datasource-list-sources 获取的 sourceConfig。仅支持 OA 审批数据源(datasourceType=OA),其他数据源类型暂不支持,待后续开放。",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "获取指定数据源下可供同步的字段列表,用于了解来源字段结构;当前仅支持全量同步。传入从 +datasource-list-sources 获取的 sourceConfig。仅支持 OA 审批数据源(datasourceType=OA),其他数据源类型暂不支持,待后续开放。",
			UseWhen:      []string{"当用户需要查看某数据源来源有哪些可同步字段、以便了解创建或更新数据源时的字段结构时使用。"},
			AvoidWhen: []string{
				"需要列出可用来源时(改用 +datasource-list-sources)",
				"需要创建数据源表时(改用 +datasource-create)",
			},
			Examples: []string{
				`dws aitable +datasource-get-fields --base-id BASE123 --datasource-type OA --source-config '{"processCode":"PROC-XXXX","name":"采购申请","dataType":"recent_time","recentDays":"30d","iconUrl":"https://example.com/icon.png","url":"https://example.com/oa"}'`,
			},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "目标 Base ID", Required: true},
		{Name: "datasource-type", Type: shortcut.FlagString, Desc: "数据源类型,目前支持审批(OA)", Required: true},
		{Name: "source-config", Type: shortcut.FlagString, Desc: "源配置 JSON 字符串。结构同 +datasource-create 的 --source-config,需含 processCode、name、iconUrl、url、dataType 及对应时间字段", Required: true},
	},
	Tips: []string{
		`dws aitable +datasource-get-fields --base-id BASE123 --datasource-type OA --source-config '{"processCode":"PROC-XXXX","name":"采购申请","dataType":"recent_time","recentDays":"30d","iconUrl":"https://example.com/icon.png","url":"https://example.com/oa"}'`,
	},
	Execute: func(rt *shortcut.RuntimeContext) error {
		if _, err := parseJSONObject("source-config", rt.Str("source-config")); err != nil {
			return err
		}
		params := map[string]any{
			"baseId":         rt.Str("base-id"),
			"datasourceType": rt.Str("datasource-type"),
			"sourceConfig":   rt.Str("source-config"),
		}
		data, err := rt.CallMCPData(serverMain, "get_datasource_fields", params)
		if err != nil {
			return err
		}
		return rt.Output(data)
	},
}

DatasourceGetFields 获取指定数据源来源的可同步字段列表(get_datasource_fields)。

View Source
var DatasourceListSources = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+datasource-list-sources",
	Product:     serverMain,
	Description: "列出指定 Base 下可用的数据源条目。仅支持 OA 审批数据源(datasourceType=OA)。返回的每条条目包含 result 字段(下游原始 JSON 字符串)和 sourceType 字段(OA 审批对应 2,仅供参考)。OA 审批场景下 result 为包含 approvals 数组的 JSON 字符串,每个 approval 包含 processCode、name、iconUrl、url、keepRemovedFields、splitParentTableField 等字段。须原样透传至 sourceConfig 的字段(仅以下 4 个):processCode、name、iconUrl、url;调用方自行设置的字段(即使 result 中有值也不透传):keepRemovedFields、splitParentTableField;enableDataSyncOaDetailList 为下游内部字段,无需传入 sourceConfig。调用方应自行解析 result,提取目标模板字段后构造 sourceConfig 传入 +datasource-create。",
	Intent:      "当用户需要查看某类数据源(如审批)的可用来源信息、获取 result/processCode 以便创建或更新数据源配置时使用。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_datasource_list_sources",
			CanonicalPath:  "aitable.shortcut_datasource_list_sources",
			CLIPath:        "aitable +datasource-list-sources",
			PrimaryCLIPath: "aitable +datasource-list-sources",
		},
		Description: "列出指定 Base 下可用的数据源条目。仅支持 OA 审批数据源(datasourceType=OA)。返回的每条条目包含 result 字段(下游原始 JSON 字符串)和 sourceType 字段(OA 审批对应 2,仅供参考)。OA 审批场景下 result 为包含 approvals 数组的 JSON 字符串,每个 approval 包含 processCode、name、iconUrl、url、keepRemovedFields、splitParentTableField 等字段。须原样透传至 sourceConfig 的字段(仅以下 4 个):processCode、name、iconUrl、url;调用方自行设置的字段(即使 result 中有值也不透传):keepRemovedFields、splitParentTableField;enableDataSyncOaDetailList 为下游内部字段,无需传入 sourceConfig。调用方应自行解析 result,提取目标模板字段后构造 sourceConfig 传入 +datasource-create。",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "列出指定 Base 下可用的数据源条目。仅支持 OA 审批数据源(datasourceType=OA)。返回的每条条目包含 result 字段(下游原始 JSON 字符串)和 sourceType 字段(OA 审批对应 2,仅供参考)。OA 审批场景下 result 为包含 approvals 数组的 JSON 字符串,每个 approval 包含 processCode、name、iconUrl、url、keepRemovedFields、splitParentTableField 等字段。须原样透传至 sourceConfig 的字段(仅以下 4 个):processCode、name、iconUrl、url;调用方自行设置的字段(即使 result 中有值也不透传):keepRemovedFields、splitParentTableField;enableDataSyncOaDetailList 为下游内部字段,无需传入 sourceConfig。调用方应自行解析 result,提取目标模板字段后构造 sourceConfig 传入 +datasource-create。",
			UseWhen:      []string{"当用户需要查看某类数据源(如审批)的可用来源信息、获取 result/processCode 以便创建或更新数据源配置时使用。"},
			AvoidWhen: []string{
				"需要创建数据源表时(改用 +datasource-create)",
				"需要获取数据源字段结构时(改用 +datasource-get-fields)",
			},
			Examples: []string{
				`dws aitable +datasource-list-sources --base-id BASE123 --datasource-type OA`,
			},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "目标 Base ID", Required: true},
		{Name: "datasource-type", Type: shortcut.FlagString, Desc: "数据源类型,目前支持审批(OA)", Required: true},
	},
	Tips: []string{
		`dws aitable +datasource-list-sources --base-id BASE123 --datasource-type OA`,
	},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":         rt.Str("base-id"),
			"datasourceType": rt.Str("datasource-type"),
		}
		data, err := rt.CallMCPData(serverMain, "list_datasource_sources", params)
		if err != nil {
			return err
		}
		return rt.Output(data)
	},
}

DatasourceListSources 列出指定数据源类型可用的来源(list_datasource_sources)。

View Source
var DatasourceSync = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+datasource-sync",
	Product:     serverMain,
	Description: "对指定 AI 表格中的数据源表触发一次手动同步。单次最多 5 张表,每张表独立提交,部分失败不影响其他表。该工具仅触发任务即返回,不会等待同步完成。返回结果包含文档链接,用户可打开文档查看同步进度与最终数据。每张表独立提交,整体仍返回 success;调用方需遍历 tasks[] 按单条 status 判断。同步运行中的表返回 failed 状态(errorCode=SYNC_RUNNING),属幂等冲突,应视为稍后重试而非最终失败。非数据源表(sync=false)不能用此工具触发同步,会以参数错误返回。",
	Intent:      "当用户需要手动触发已有数据源表的同步(而非创建或更新配置)时使用。同步任务 ID 可通过 +datasource-sync-status 查询结果。",
	Risk:        shortcut.RiskWrite,
	Safety: contract.SafetySpec{
		Effect: "write", Risk: "medium",
		Confirmation: "not_required", Idempotency: "non_idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_datasource_sync",
			CanonicalPath:  "aitable.shortcut_datasource_sync",
			CLIPath:        "aitable +datasource-sync",
			PrimaryCLIPath: "aitable +datasource-sync",
		},
		Description: "对指定 AI 表格中的数据源表触发一次手动同步。单次最多 5 张表,每张表独立提交,部分失败不影响其他表。该工具仅触发任务即返回,不会等待同步完成。返回结果包含文档链接,用户可打开文档查看同步进度与最终数据。每张表独立提交,整体仍返回 success;调用方需遍历 tasks[] 按单条 status 判断。同步运行中的表返回 failed 状态(errorCode=SYNC_RUNNING),属幂等冲突,应视为稍后重试而非最终失败。非数据源表(sync=false)不能用此工具触发同步,会以参数错误返回。",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "对指定 AI 表格中的数据源表触发一次手动同步。单次最多 5 张表,每张表独立提交,部分失败不影响其他表。该工具仅触发任务即返回,不会等待同步完成。返回结果包含文档链接,用户可打开文档查看同步进度与最终数据。每张表独立提交,整体仍返回 success;调用方需遍历 tasks[] 按单条 status 判断。同步运行中的表返回 failed 状态(errorCode=SYNC_RUNNING),属幂等冲突,应视为稍后重试而非最终失败。非数据源表(sync=false)不能用此工具触发同步,会以参数错误返回。",
			UseWhen:      []string{"当用户需要手动触发已有数据源表的同步(而非创建或更新配置)时使用。同步任务 ID 可通过 +datasource-sync-status 查询结果。"},
			AvoidWhen: []string{
				"需要创建新数据源表时(改用 +datasource-create)",
				"需要更新配置时(改用 +datasource-update)",
			},
			Examples: []string{
				`dws aitable +datasource-sync --base-id BASE123 --table-ids TBL1,TBL2`,
				`dws aitable +datasource-sync --base-id BASE123 --table-ids TBL1`,
			},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "目标 Base ID", Required: true},
		{Name: "table-ids", Type: shortcut.FlagStringSlice, Desc: "待触发同步的数据源表 ID 列表(通过 +base-get / +table-list 获取,仅允许 sync=true 的表,1-5 个)", Required: true},
	},
	Tips: []string{
		`dws aitable +datasource-sync --base-id BASE123 --table-ids TBL1,TBL2`,
		`dws aitable +datasource-sync --base-id BASE123 --table-ids TBL1`,
	},
	Execute: func(rt *shortcut.RuntimeContext) error {
		rawTableIDs := rt.StrSlice("table-ids")
		tableIDs := trimNonEmpty(rawTableIDs)
		if len(tableIDs) < 1 || len(tableIDs) > 5 {
			return fmt.Errorf("--table-ids requires 1-5 table IDs, got %d", len(tableIDs))
		}
		params := map[string]any{
			"baseId":   rt.Str("base-id"),
			"tableIds": tableIDs,
		}
		data, err := rt.CallMCPData(serverMain, "run_datasource_sync", params)
		if err != nil {
			return err
		}
		return rt.Output(data)
	},
}

DatasourceSync 对数据源表触发一次手动同步(run_datasource_sync)。

View Source
var DatasourceSyncStatus = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+datasource-sync-status",
	Product:     serverMain,
	Description: "按任务 ID 查询指定数据源表的同步任务状态。与 +datasource-sync / +datasource-create / +datasource-update 配对使用,单次最多 5 个 taskId。仅 RUNNING 需要继续轮询;FINISHED、FAILED、NOT_FOUND 为终态;UNKNOWN 表示状态不可断言,也不应继续轮询。批量查询整体 success 时仍需逐项检查 tasks[].status 和 errorCode。",
	Intent:      "当用户触发同步后需要按 taskId 查询同步是否完成、成功或失败时使用。支持批量查询(单次最多 5 个任务 ID)。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_datasource_sync_status",
			CanonicalPath:  "aitable.shortcut_datasource_sync_status",
			CLIPath:        "aitable +datasource-sync-status",
			PrimaryCLIPath: "aitable +datasource-sync-status",
		},
		Description: "按任务 ID 查询数据源同步状态。仅 RUNNING 继续轮询;FINISHED、FAILED、NOT_FOUND 为终态;UNKNOWN 不可断言且不继续轮询。整体 success 时仍需逐项检查 tasks[]。",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "按任务 ID 查询指定数据源表的同步任务状态。与 +datasource-sync / +datasource-create / +datasource-update 配对使用,单次最多 5 个 taskId。仅 RUNNING 需要继续轮询;FINISHED、FAILED、NOT_FOUND 为终态;UNKNOWN 表示状态不可断言,也不应继续轮询。批量查询整体 success 时仍需逐项检查 tasks[].status 和 errorCode。",
			UseWhen:      []string{"当用户触发同步后需要按 taskId 查询同步是否完成、成功或失败时使用。支持批量查询(单次最多 5 个任务 ID)。"},
			AvoidWhen: []string{
				"需要触发同步时(改用 +datasource-sync)",
			},
			Examples: []string{
				`dws aitable +datasource-sync-status --base-id BASE123 --table-id TBL456 --task-ids TASK1,TASK2`,
			},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "目标 Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "数据源表 ID(通过 +base-get / +table-list 获取,仅允许传入 sync=true 的表)", Required: true},
		{Name: "task-ids", Type: shortcut.FlagStringSlice, Desc: "待查询的同步任务 ID 列表(由 +datasource-sync / +datasource-create / +datasource-update 返回)。单次最多 5 个,超出请拆分多次调用。", Required: true},
	},
	Tips: []string{
		`dws aitable +datasource-sync-status --base-id BASE123 --table-id TBL456 --task-ids TASK1,TASK2`,
	},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
		}
		rawTaskIDs := rt.StrSlice("task-ids")
		taskIDs := trimNonEmpty(rawTaskIDs)
		if len(taskIDs) < 1 || len(taskIDs) > 5 {
			return fmt.Errorf("--task-ids requires 1-5 task IDs, got %d", len(taskIDs))
		}
		params["taskIds"] = taskIDs
		data, err := rt.CallMCPData(serverMain, "get_datasource_sync_status", params)
		if err != nil {
			return err
		}
		return rt.Output(data)
	},
}

DatasourceSyncStatus 查询数据源表同步任务状态(get_datasource_sync_status)。

View Source
var DatasourceUpdate = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+datasource-update",
	Product:     serverMain,
	Description: "更新已有数据源表的完整源配置或自动同步设置,并触发一次全量同步。仅适用于数据源表,当前不支持选择同步字段。",
	Intent:      "当用户需要更换审批模板、调整同步时间范围或开关自动同步时使用;source-config 可省略,此时先读取当前配置并原样提交,当前仅支持全量同步。",
	Risk:        shortcut.RiskWrite,
	Safety: contract.SafetySpec{
		Effect: "write", Risk: "medium",
		Confirmation: "not_required", Idempotency: "non_idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_datasource_update",
			CanonicalPath:  "aitable.shortcut_datasource_update",
			CLIPath:        "aitable +datasource-update",
			PrimaryCLIPath: "aitable +datasource-update",
		},
		Description: "更新已有数据源表的完整源配置或自动同步设置,并触发一次全量同步。仅适用于数据源表,当前不支持选择同步字段。",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "The CLI reads aitable/get_datasource_config when --source-config is omitted, preserves its complete sourceConfig, and then calls aitable/update_datasource_config; no single direct MCP interface represents this compatibility adapter.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "更新已有数据源表的完整源配置或自动同步设置,并触发一次全量同步。仅适用于数据源表,当前不支持选择同步字段。",
			UseWhen:      []string{"当用户需要更换审批模板、调整同步时间范围或开关自动同步时使用;source-config 可省略,此时先读取当前配置并原样提交,当前仅支持全量同步。"},
			AvoidWhen: []string{
				"需要创建新数据源表时(改用 +datasource-create)",
				"仅需触发同步不改配置时(改用 +datasource-sync)",
			},
			Examples: []string{
				`dws aitable +datasource-update --base-id BASE123 --table-id TBL456 --auto`,
				`dws aitable +datasource-update --base-id BASE123 --table-id TBL456 --source-config '{"processCode":"PROC-YYYY","name":"出差申请","dataType":"recent_time","recentDays":"30d","iconUrl":"https://example.com/icon.png","url":"https://example.com/oa"}'`,
			},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "目标 Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "已存在的数据源表 ID(通过 +base-get / +table-list 获取,仅允许传入 sync=true 的数据源表)", Required: true},
		{Name: "source-config", Type: shortcut.FlagString, Desc: "可选。省略时先读取当前 sourceConfig 并原样提交,读取失败则不更新;显式传入时整体覆盖。字段分为两类:须从 +datasource-list-sources 结果原样透传的字段(必填):processCode(审批流程编码)、name(展示名称)、iconUrl(图标 URL)、url(跳转链接);调用方自行设置的字段:dataType(必填,time_range/start_time/recent_time)、recentDays(dataType=recent_time 时有效,7d/30d/1y,默认 30d)、startDate(dataType=time_range/start_time 时有效,yyyy-MM-dd,默认 30 天前)、endDate(dataType=time_range 时有效,yyyy-MM-dd,默认当天)、keepRemovedFields(默认 false)。约定:syncAll 固定为 true;splitParentTableField 与 enableDataSyncOaDetailList 为下游内部字段,无需传入"},
		{Name: "auto", Type: shortcut.FlagBool, Desc: "可选。是否开启自动同步;仅显式设置时下发给下游,省略时保持原有自动同步开关不变"},
		{Name: "field-ids", Type: shortcut.FlagStringSlice, Desc: "不受支持:当前仅支持全量同步,请勿传入"},
		{Name: "auto-sync-setting", Type: shortcut.FlagString, Desc: "可选。自动同步频率配置 JSON 字符串,仅在显式设置 --auto=true 时生效;省略时保持原有自动同步频率配置。字段:syncType(必填,hourly=按小时间隔,scheduled=定时触发)、hourlyInterval(syncType=hourly 时必填,正整数小时)、scheduleType(syncType=scheduled 时必填,daily/weekly/monthly)、timeValue(syncType=scheduled 时必填,HH:mm)、selectedMonthDays(scheduleType=monthly 时必填,每月几号触发,1-31)、selectedWeekdays(scheduleType=weekly 时必填,每周哪几天触发,1=周一…7=周日)、skipNonWorkingDay(可选,默认 false)"},
	},
	Tips: []string{
		`dws aitable +datasource-update --base-id BASE123 --table-id TBL456 --auto`,
		`dws aitable +datasource-update --base-id BASE123 --table-id TBL456 --source-config '{"processCode":"PROC-YYYY","name":"出差申请","dataType":"recent_time","recentDays":"30d","iconUrl":"https://example.com/icon.png","url":"https://example.com/oa"}'`,
	},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
		}
		if rt.Changed("source-config") {
			if _, err := parseJSONObject("source-config", rt.Str("source-config")); err != nil {
				return err
			}
			params["sourceConfig"] = rt.Str("source-config")
		}
		if rt.Changed("auto") {
			params["auto"] = rt.Bool("auto")
		}
		if rt.Changed("field-ids") {
			return apperrors.NewValidation("--field-ids 不受支持:当前数据源仅支持全量同步,请移除此参数")
		}
		if rt.Changed("auto-sync-setting") {
			v := rt.Str("auto-sync-setting")
			if v == "" {
				return fmt.Errorf("--auto-sync-setting 显式提供时不能为空,如需保持默认请勿传入")
			}
			if _, err := parseJSONObject("auto-sync-setting", v); err != nil {
				return err
			}
			params["autoSyncSetting"] = v
		}

		if !rt.Changed("source-config") {
			if !rt.Changed("auto") && !rt.Changed("auto-sync-setting") {
				return apperrors.NewValidation("至少需要一个配置变更:--source-config、--auto 或 --auto-sync-setting;仅触发同步请使用 +datasource-sync")
			}
			current, err := rt.CallMCPReadData(serverMain, "get_datasource_config", map[string]any{
				"baseId": rt.Str("base-id"), "tableId": rt.Str("table-id"),
			})
			if err != nil {
				return err
			}
			config, err := aitableprotocol.DatasourceSourceConfig(current)
			if err != nil {
				return apperrors.NewAPI(err.Error()+";未执行数据源更新,请显式提供完整 --source-config",
					apperrors.WithOperation("aitable/get_datasource_config"),
					apperrors.WithReason("datasource_config_unavailable"),
					apperrors.WithFailureStage("response_validation"),
					apperrors.WithExecutionStarted(false),
					apperrors.WithRetryable(false))
			}
			params["sourceConfig"] = config
		}
		if err := rt.Command().Context().Err(); err != nil {
			return err
		}
		data, err := rt.CallMCPWriteData(serverMain, "update_datasource_config", params)
		if err != nil {
			return err
		}
		return rt.Output(data)
	},
}

DatasourceUpdate 更新已有数据源表的同步配置(update_datasource_config)。

View Source
var ExportData = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+export-data",
	Product:     serverMain,
	Description: "导出 AI 表格数据(创建导出任务或按 taskId 续等)",
	Intent:      "当你要把 AI 表格数据导出为 Excel/附件(可选整个 Base、某张表或某个视图),或用已有 taskId 继续等待之前导出任务完成时使用;返回导出任务状态与下载结果。",
	Risk:        shortcut.RiskRead,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "task-id", Type: shortcut.FlagString, Desc: "已有导出任务 ID(传入则续等,不重新创建)"},
		{Name: "scope", Type: shortcut.FlagString, Desc: "导出范围", Enum: []string{"all", "table", "view"}},
		{Name: "format", Type: shortcut.FlagString, Desc: "导出格式",
			Enum: []string{"excel", "attachment", "excel_and_attachment", "excel_with_inline_images"}},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID(scope=table/view 时)"},
		{Name: "view-id", Type: shortcut.FlagString, Desc: "View ID(scope=view 时)"},
		{Name: "timeout-ms", Type: shortcut.FlagInt, Desc: "同步等待超时(毫秒,可选)"},
	},
	Tips: []string{`dws aitable +export-data --base-id B --scope all --format excel`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{"baseId": rt.Str("base-id")}
		if rt.Changed("task-id") {
			params["taskId"] = rt.Str("task-id")
		} else {
			if !rt.Changed("scope") || !rt.Changed("format") {
				return fmt.Errorf("需指定 --task-id,或同时指定 --scope 和 --format")
			}
			params["scope"] = rt.Str("scope")
			params["format"] = rt.Str("format")
		}
		if rt.Changed("table-id") {
			params["tableId"] = rt.Str("table-id")
		}
		if rt.Changed("view-id") {
			params["viewId"] = rt.Str("view-id")
		}
		if rt.Changed("timeout-ms") {
			params["timeoutMs"] = rt.Int("timeout-ms")
		}
		return rt.CallMCP("export_data", params)
	},
}

ExportData 导出数据(export_data)。

View Source
var FieldCreate = shortcut.Shortcut{
	Service: "aitable", Command: "+field-create", Product: serverMain,
	Description: "批量新增字段,15 个分片并核对新字段 ID、类型与配置",
	Intent:      "已有数据表要新增明确结构的一批字段时使用;名称已存在或重复则停止。创建回执明确含 CREATE_FIELD_READBACK_PENDING 且保留 fieldId 时,表示写入已受理但读回尚未验证,不代表未创建;只对这些原 ID 有界读回并核对名称、类型与配置。读回预算耗尽仍保留已知 ID 和未验证状态,不重发创建。仅 fieldId 非空不足以证明任意失败回执已成功。",
	Risk:        shortcut.RiskWrite,
	Safety:      contract.SafetySpec{Effect: "write", Risk: "medium", Confirmation: "user_required", Idempotency: "non_idempotent"},
	Contract:    aitableCompositeContractWithResult("+field-create", "批量新增字段并核对结构;读回延迟保留原 ID", "已有表新增一批不重名字段,或核对创建后读回延迟的回执时", "已有字段修改用 +field-update;新建整张表用 +table-bootstrap", `dws aitable +field-create --base-id B --table-id T --fields '[{"fieldName":"标题","type":"text"}]'`, parityCompositeResultSpec()),
	Flags:       []shortcut.Flag{{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true}, {Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true}, {Name: "fields", Type: shortcut.FlagString, Desc: "非空字段结构数组,fieldName/type/config/description/aiConfig,最多 100 个", Required: true}, {Name: "resume-field-ids", Type: shortcut.FlagStringSlice, Desc: "提供 fields 开头已创建字段的 ID;核实名称与结构后按 fields 顺序返回,只创建剩余字段,全部提供时只核实"}},
	Execute:     executeParityFieldCreate,
}
View Source
var FieldDelete = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+field-delete",
	Product:     serverMain,
	Description: "删除指定字段(不可逆)",
	Intent:      "当你确认要删除某个字段(连同该列在所有记录里的数据)时使用;不可逆,操作前请核对 fieldId。",
	Risk:        shortcut.RiskHighWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "field-id", Type: shortcut.FlagString, Desc: "待删除字段 ID", Required: true},
	},
	Tips: []string{`dws aitable +field-delete --base-id B --table-id T --field-id F`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("delete_field", map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
			"fieldId": rt.Str("field-id"),
			"confirm": true,
		})
	},
}

FieldSearchOptions 搜索单选/多选字段选项(search_field_options)。 FieldDelete 删除字段(delete_field)。

View Source
var FieldGet = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+field-get",
	Product:     serverMain,
	Description: "批量获取字段详情(含类型相关完整配置)",
	Intent:      "当你需要查看字段的完整类型配置(如单选选项、关联表设置、AI 配置)以便正确写入数据或改配置时使用;批量返回字段详情。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_field_get",
			CanonicalPath:  "aitable.shortcut_field_get",
			CLIPath:        "aitable +field-get",
			PrimaryCLIPath: "aitable +field-get",
		},
		Description: "批量获取字段详情(含类型相关完整配置)",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "批量获取字段详情(含类型相关完整配置)",
			UseWhen:      []string{"当你需要查看字段的完整类型配置(如单选选项、关联表设置、AI 配置)以便正确写入数据或改配置时使用;批量返回字段详情。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples:     []string{"dws aitable +field-get --base-id B --table-id T"},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "field-ids", Type: shortcut.FlagStringSlice, Desc: "字段 ID 列表,逗号分隔,单次最多 10 个(可选)"},
	},
	Tips: []string{`dws aitable +field-get --base-id B --table-id T`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
		}
		if rt.Changed("field-ids") {
			params["fieldIds"] = rt.StrSlice("field-ids")
		}
		return rt.CallMCP("get_fields", params)
	},
}

FieldGet 获取字段详情(get_fields)。

View Source
var FieldRunAI = shortcut.Shortcut{Service: "aitable", Command: "+field-run-ai", Product: serverMain, Description: "提交 AI 字段运行请求;缺少可靠受理证据时非零退出并保留回执", Intent: "已有 AI 字段需要运行整列或指定记录时;不把受理或重复运行冲突当成计算完成。", Risk: shortcut.RiskWrite, Safety: contract.SafetySpec{Effect: "write", Risk: "medium", Confirmation: "user_required", Idempotency: "unknown"}, Contract: aitableCompositeContractWithResult("+field-run-ai", "提交 AI 字段运行请求并保留逐字段原始回执", "触发已有 AI 字段运行时", "读取计算值用 +record-query;配置 AI 用 +field-update --ai-config;通用扩展安装不走本入口", `dws aitable +field-run-ai --base-id B --table-id T --field-ids F --record-ids R`, &contract.ResultSpec{Outcomes: []contract.ResultOutcome{contract.ResultOutcomeSuccess, contract.ResultOutcomeFailure}, DataSchema: json.RawMessage(`{"type":"object","properties":{"status":{"type":"string","description":"submitted 表示逐字段任务已受理;submitted_unverified 表示无法核实;两者均不代表计算完成"},"completed":{"type":"boolean","description":"始终 false,服务不等待计算完成"},"fieldIds":{"type":"array","description":"提交的字段 ID","items":{"type":"string"}},"receipt":{"type":"object","description":"下游原始回执,保留每个字段的冲突或错误"}},"required":["status","completed","fieldIds","receipt"]}`)}), Flags: []shortcut.Flag{{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true}, {Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true}, {Name: "field-ids", Type: shortcut.FlagStringSlice, Desc: "1-10 个 AI 字段 ID", Required: true}, {Name: "record-ids", Type: shortcut.FlagStringSlice, Desc: "1-500 个记录 ID;省略整列运行"}}, Execute: func(rt *shortcut.RuntimeContext) error {
	fields, err := parseRecordIDs(rt.StrSlice("field-ids"))
	if err != nil || len(fields) > 10 {
		return apperrors.NewValidation("field-ids 必须为 1-10 个不同 ID")
	}
	args := map[string]any{"baseId": rt.Str("base-id"), "tableId": rt.Str("table-id"), "fieldIds": fields}
	if rt.Changed("record-ids") {
		ids, e := parseRecordIDs(rt.StrSlice("record-ids"))
		if e != nil || len(ids) > 500 {
			return apperrors.NewValidation("record-ids 必须为 1-500 个不同 ID")
		}
		args["recordIds"] = ids
	}
	if rt.DryRun() {
		return rt.Output(map[string]any{"executed": false, "arguments": args})
	}
	raw, err := rt.CallMCPData(serverMain, "get_fields", map[string]any{"baseId": args["baseId"], "tableId": args["tableId"], "fieldIds": fields})
	if err != nil {
		return err
	}
	actual, ok := findNamedObjectList(raw, "fields")
	if !ok {
		return fmt.Errorf("get_fields lacks fields")
	}
	for _, id := range fields {
		matches := 0
		for _, f := range actual {
			if stringValue(f, "fieldId") == id {
				matches++
				config, ok := f["aiConfig"].(map[string]any)
				if !ok || len(config) == 0 {
					return apperrors.NewValidation("字段尚未读到 aiConfig;请先完成配置回读再运行")
				}
			}
		}
		if matches != 1 {
			return apperrors.NewValidation("AI 字段不存在或不唯一")
		}
	}
	receipt, err := rt.CallMCPWriteDataStrict(serverMain, "run_ai_field", args)
	if err != nil {
		return err
	}
	if aiTasksSubmitted(receipt, fields) {
		return rt.Output(map[string]any{"status": "submitted", "completed": false, "fieldIds": fields, "receipt": receipt})
	}
	return apperrors.NewAPI("AI 运行请求已返回,但下游没有可核实的逐字段受理或任务状态;请核查回执与目标记录,不要直接重试",
		apperrors.WithReason("aitable_ai_submission_unverified"),
		apperrors.WithExecutionStarted(true), apperrors.WithRetryable(false),
		apperrors.WithDetails(map[string]any{"result": map[string]any{"status": "submitted_unverified", "completed": false, "fieldIds": fields, "receipt": receipt}}),
	)
}}
View Source
var FieldUpdate = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+field-update",
	Product:     serverMain,
	Description: "更新字段名称 / 说明 / 配置 / AI 配置(类型不可改)",
	Intent:      "当你要改字段名,或调整字段配置/AI 配置(注意字段类型本身不可改)时使用;会实际更新指定字段。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "field-id", Type: shortcut.FlagString, Desc: "Field ID", Required: true},
		{Name: "name", Type: shortcut.FlagString, Desc: "新字段名(可选)"},
		{Name: "description", Type: shortcut.FlagString, Desc: "字段说明;显式空字符串清除说明,省略保留"},
		{Name: "config", Type: shortcut.FlagString, Desc: "字段配置 JSON(可选)"},
		{Name: "ai-config", Type: shortcut.FlagString, Desc: "AI 配置 JSON(可选)"},
	},
	Tips: []string{`dws aitable +field-update --base-id B --table-id T --field-id F --name "新名"`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
			"fieldId": rt.Str("field-id"),
		}
		if rt.Changed("name") {
			if err := aitableprotocol.ValidateFieldName(rt.Str("name")); err != nil {
				return fmt.Errorf("--name: %w", err)
			}
			params["newFieldName"] = rt.Str("name")
		}
		if rt.Changed("description") {
			params["description"], _ = rt.Command().Flags().GetString("description")
		}
		if rt.Changed("config") {
			cfg, err := parseJSONObject("config", rt.Str("config"))
			if err != nil {
				return err
			}
			params["config"] = cfg
		}
		if rt.Changed("ai-config") {
			cfg, err := parseJSONObject("ai-config", rt.Str("ai-config"))
			if err != nil {
				return err
			}
			params["aiConfig"] = cfg
		}
		return rt.CallMCP("update_field", params)
	},
}

FieldCreate 创建字段(create_fields)。 FieldUpdate 更新字段(update_field)。

View Source
var FormDelete = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+form-delete",
	Product:     serverHelper,
	Description: "删除指定表单视图(不可逆)",
	Intent:      "当你确认要删除某个收集表单时使用;不可逆,仅删除该表单视图。",
	Risk:        shortcut.RiskHighWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "view-id", Type: shortcut.FlagString, Desc: "表单 View ID", Required: true},
	},
	Tips: []string{`dws aitable +form-delete --base-id B --table-id T --view-id V`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("delete_form_view", map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
			"viewId":  rt.Str("view-id"),
		})
	},
}

FormDelete 删除表单(delete_form_view,server: aitable)。

View Source
var FormFieldHide = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+form-field-hide",
	Product:     serverHelper,
	Description: "切换表单字段的隐藏/显示状态",
	Intent:      "当你要在表单里隐藏或重新显示某个字段时使用;会实际切换该字段在表单中的显示状态。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "view-id", Type: shortcut.FlagString, Desc: "表单 View ID", Required: true},
		{Name: "field-id", Type: shortcut.FlagString, Desc: "Field ID", Required: true},
		{Name: "hidden", Type: shortcut.FlagBool, Desc: "true 隐藏 / false 显示", Required: true},
	},
	Tips: []string{`dws aitable +form-field-hide --base-id B --table-id T --view-id V --field-id F --hidden true`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("update_form_field_hidden", map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
			"viewId":  rt.Str("view-id"),
			"fieldId": rt.Str("field-id"),
			"hidden":  rt.Bool("hidden"),
		})
	},
}

FormFieldHide 切换表单字段隐藏(update_form_field_hidden,server: aitable)。

View Source
var FormFieldList = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+form-field-list",
	Product:     serverHelper,
	Description: "列出表单视图当前可见的字段及其配置",
	Intent:      "当你要查看某表单当前放出了哪些字段供填写及其是否必填等配置时使用;返回表单可见字段列表。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_form_field_list",
			CanonicalPath:  "aitable.shortcut_form_field_list",
			CLIPath:        "aitable +form-field-list",
			PrimaryCLIPath: "aitable +form-field-list",
		},
		Description: "列出表单视图当前可见的字段及其配置",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "列出表单视图当前可见的字段及其配置",
			UseWhen:      []string{"当你要查看某表单当前放出了哪些字段供填写及其是否必填等配置时使用;返回表单可见字段列表。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples:     []string{"dws aitable +form-field-list --base-id B --table-id T --view-id V"},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "view-id", Type: shortcut.FlagString, Desc: "表单 View ID", Required: true},
	},
	Tips: []string{`dws aitable +form-field-list --base-id B --table-id T --view-id V`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("list_form_fields", map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
			"viewId":  rt.Str("view-id"),
		})
	},
}

FormFieldList 列出表单字段(list_form_fields,server: aitable)。

View Source
var FormFieldUpdate = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+form-field-update",
	Product:     serverHelper,
	Description: "更新表单字段的必填状态或描述",
	Intent:      "当你要把表单里某个字段设为必填/非必填,或补充其填写说明时使用;会实际更新该表单字段的配置。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "view-id", Type: shortcut.FlagString, Desc: "表单 View ID", Required: true},
		{Name: "field-id", Type: shortcut.FlagString, Desc: "Field ID", Required: true},
		{Name: "required", Type: shortcut.FlagBool, Desc: "是否必填(可选)"},
		{Name: "field-description", Type: shortcut.FlagString, Desc: "字段描述(可选)"},
	},
	Tips: []string{`dws aitable +form-field-update --base-id B --table-id T --view-id V --field-id F --required true`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
			"viewId":  rt.Str("view-id"),
			"fieldId": rt.Str("field-id"),
		}
		if rt.Changed("required") {
			params["required"] = rt.Bool("required")
		}
		if rt.Changed("field-description") {
			params["fieldDescription"] = rt.Str("field-description")
		}
		return rt.CallMCP("update_form_field", params)
	},
}

FormFieldUpdate 更新表单字段(update_form_field,server: aitable)。

View Source
var FormList = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+form-list",
	Product:     serverHelper,
	Description: "列出指定数据表下的所有表单视图",
	Intent:      "当你要查看某数据表下已有哪些收集表单时使用;返回该表的全部表单视图列表。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_form_list",
			CanonicalPath:  "aitable.shortcut_form_list",
			CLIPath:        "aitable +form-list",
			PrimaryCLIPath: "aitable +form-list",
		},
		Description: "列出指定数据表下的所有表单视图",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "列出指定数据表下的所有表单视图",
			UseWhen:      []string{"当你要查看某数据表下已有哪些收集表单时使用;返回该表的全部表单视图列表。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples:     []string{"dws aitable +form-list --base-id B --table-id T"},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
	},
	Tips: []string{`dws aitable +form-list --base-id B --table-id T`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		data, err := rt.CallMCPData(serverHelper, "list_form_views", map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
		})
		if err != nil {
			return err
		}
		forms, err := formListProject(data)
		if err != nil {
			return err
		}
		return rt.Output(map[string]any{"count": len(forms), "forms": forms})
	},
}

FormCreate 创建表单视图(create_view, viewType=FormDesigner,server: aitable)。 FormList 列出表单视图(list_form_views,server: aitable)。

View Source
var FormShareGet = shortcut.Shortcut{
	OutputRollout: output.RolloutUnifiedActive,
	Service:       "aitable",
	Command:       "+form-share-get",
	Product:       serverHelper,
	Description:   "读取表单分享配置及服务端真实 UUID、状态和封面",
	Intent:        "当你要查看某视图是否已分享,或诊断 shareFormUuid、status、formCover 时使用;该命令只读,不修改 CP。",
	Risk:          shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_form_share_get",
			CanonicalPath:  "aitable.shortcut_form_share_get",
			CLIPath:        "aitable +form-share-get",
			PrimaryCLIPath: "aitable +form-share-get",
		},
		Description: "读取表单分享配置及服务端真实 UUID、状态和封面",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "读取表单分享配置及服务端真实 UUID、状态和封面",
			UseWhen:      []string{"当你要查看某视图是否已分享,或诊断 shareFormUuid、status、formCover 时使用;该命令只读,不修改 CP。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples:     []string{"dws aitable +form-share-get --base-id B --table-id T --view-id V"},
		},
		Result: helpers.AitableFormShareGetResultSpec(),
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "view-id", Type: shortcut.FlagString, Desc: "View ID", Required: true},
	},
	Tips: []string{`dws aitable +form-share-get --base-id B --table-id T --view-id V`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return executeFormShareMCP(rt, "get_share_form_config", map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
			"viewId":  rt.Str("view-id"),
		}, false)
	},
}

FormShareGet 获取表单分享配置(get_share_form_config,server: aitable)。

View Source
var FormShareUpdate = shortcut.Shortcut{
	OutputRollout: output.RolloutUnifiedActive,
	Service:       "aitable",
	Command:       "+form-share-update",
	Product:       serverHelper,
	Description:   "部分更新分享表单配置,并返回经服务端回读和 CP 投影校验的真实终态",
	Intent:        "回答契约:用户仅询问用法时,最终回答必须先给出完整命令;缺少必填 ID 时则给出带明确占位符的完整命令模板,禁止猜测。随后明确说明“未传入的分享配置保持原值”;不得执行目标写操作或声称已经执行。发现门禁:即使 Skill 或参考文档已提供完整示例,回答前也必须实际执行一次且仅执行一次目标 leaf 的安全 help/schema 查询;不得仅依据 Skill 或参考文档直接作答。第一行已有的必填值必须原样使用,缺少的值保留为 <BASE_ID>、<TABLE_ID>、<VIEW_ID> 等明确占位符;仅当存在占位符时第二行才说明需要替换的占位符,已知短 ID 也不能要求替换。最终只输出两行纯文本,无标题或代码围栏;第一行必须保留 --format json;所有 ID 已知时第二行原样为:未传入的分享配置保持原值。本次仅查询 help/schema,未执行写操作。仅询问写法时唯一允许的查询是 dws schema --cli-path \"aitable +form-share-update\" --compact --format json,成功后不再查询 help/schema。当你要开启、关闭或调整表单分享配置时使用;新建表单首次开启分享且已知标题时,同一次调用传入 --form-name;未传入的配置保持原值。成功后检查 shareFormUuid、status、formCover、cpSynced,只有 cpSynced=true 才表示闭环完成;cpSynced=false/缺失/类型错误或其他必需字段无效时返回 partial_failure 和退出码7;原始回执在 data.succeeded[0].response,失败信息在 data.failed[0].error,含 execution_started=true,不自动重放写入;部分失败不得当作成功;DWS 不自行调用第二个 View 更新命令补偿 CP。get 不返回 cpSynced,不能用 get 回读该字段或确认 CP 已恢复;只能诊断分享配置,CP 未确认时需服务端诊断。即使外层仍为 ok=true 或返回结构不符合契约,也不得为再次校验 CP 而执行或建议重发 form share update / +form-share-update(包括稍后传相同配置);诊断不能新增写入,只保留回执并交由服务端排查。",
	Risk:          shortcut.RiskWrite,
	Safety: contract.SafetySpec{
		Effect: "write", Risk: "medium",
		Confirmation: "user_required", Idempotency: "unknown",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_form_share_update",
			CanonicalPath:  "aitable.shortcut_form_share_update",
			CLIPath:        "aitable +form-share-update",
			PrimaryCLIPath: "aitable +form-share-update",
		},
		Description: "部分更新分享表单配置,并返回经服务端回读和 CP 投影校验的真实终态",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "部分更新表单分享配置,并返回真实 UUID、状态、封面及 CP 同步结果",
			UseWhen:      []string{"需要通过内置 +form-share-update Shortcut 开启、关闭或调整表单分享配置时;成功后检查 shareFormUuid、status、formCover、cpSynced,新建表单已知标题时同一次调用传入 --form-name"},
			AvoidWhen:    []string{"只查询用 +form-share-get;DWS 不自行调用第二个 View 更新命令补偿 CP"},
			Examples:     []string{`dws aitable +form-share-update --base-id B --table-id T --view-id V --enabled true --form-name "活动报名" --format json`},
		},
		Result: helpers.AitableFormShareUpdateResultSpec(),
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "view-id", Type: shortcut.FlagString, Desc: "View ID", Required: true},

		{Name: "enabled", Type: shortcut.FlagString, Desc: "分享开关", Enum: []string{"true", "false"}},
		{Name: "auth-type-code", Type: shortcut.FlagInt, Desc: "授权类型 code:0 企业内成员,1 钉钉用户,2 指定成员,3/4/5 为教育业务"},
		{Name: "auth-data", Type: shortcut.FlagString, Desc: "授权内容;auth-type-code=2 时为逗号分隔的成员 ID"},
		{Name: "submit-times-limit", Type: shortcut.FlagInt, Desc: "所有人合计提交次数上限"},
		{Name: "submit-times-user-limit", Type: shortcut.FlagInt, Desc: "单用户提交限制 code:0 不限制,1 仅一次,2 每天一次,3 每周期一次"},
		{Name: "form-start-time", Type: shortcut.FlagInt, Desc: "表单生效时间,毫秒时间戳"},
		{Name: "form-end-time", Type: shortcut.FlagInt, Desc: "表单失效时间,毫秒时间戳"},
		{Name: "form-name", Type: shortcut.FlagString, Desc: "分享表单名称;新建表单首次开启分享时传入已知标题"},
		{Name: "form-desc", Type: shortcut.FlagString, Desc: "分享表单描述"},
		{Name: "anonymous-submit", Type: shortcut.FlagString, Desc: "是否允许匿名提交", Enum: []string{"true", "false"}},
		{Name: "load-last-submit", Type: shortcut.FlagString, Desc: "重新打开时是否加载上次提交", Enum: []string{"true", "false"}},
		{Name: "reply-notice", Type: shortcut.FlagString, Desc: "有人填写后是否通知分享人", Enum: []string{"true", "false"}},
		{Name: "share-uid-list", Type: shortcut.FlagString, Desc: "接收回复通知的钉钉 uid,逗号分隔"},
	},
	Constraints: []shortcut.Constraint{{
		Kind:         shortcut.ConstraintAtLeastOne,
		PresenceOnly: true,
		Description:  "至少显式传入一个更新参数;空字符串也算更新,未传参数保持原值",
		Flags: []string{
			"enabled", "auth-type-code", "auth-data", "submit-times-limit", "submit-times-user-limit",
			"form-start-time", "form-end-time", "form-name", "form-desc", "anonymous-submit",
			"load-last-submit", "reply-notice", "share-uid-list",
		},
	}},
	Tips: []string{
		`dws aitable +form-share-update --base-id B --table-id T --view-id V --enabled true --form-name "活动报名" --format json`,
		`dws aitable +form-share-update --base-id B --table-id T --view-id V --form-name "活动报名" --anonymous-submit true --format json`,
	},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
			"viewId":  rt.Str("view-id"),
		}
		for name, property := range map[string]string{
			"enabled": "enabled", "anonymous-submit": "anonymousSubmit",
			"load-last-submit": "loadLastSubmit", "reply-notice": "replyNotice",
		} {
			if rt.Changed(name) {
				params[property] = strings.EqualFold(rt.Str(name), "true")
			}
		}
		for name, property := range map[string]string{
			"auth-type-code": "authTypeCode", "submit-times-limit": "submitTimesLimit",
			"submit-times-user-limit": "submitTimesUserLimit", "form-start-time": "formStartTime",
			"form-end-time": "formEndTime",
		} {
			if rt.Changed(name) {
				params[property] = rt.Int(name)
			}
		}
		for name, property := range map[string]string{
			"auth-data": "authData", "form-name": "formName", "form-desc": "formDesc",
			"share-uid-list": "shareUidList",
		} {
			if rt.Changed(name) {
				params[property] = rt.Str(name)
			}
		}
		return executeFormShareMCP(rt, "update_share_form", params, true)
	},
}

FormShareUpdate 更新分享表单配置(update_share_form,server: aitable)。

View Source
var FormUpdate = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+form-update",
	Product:     serverHelper,
	Description: "更新表单标题 / 描述",
	Intent:      "当你要修改表单对外展示的标题或说明文案时使用;会实际更新表单的标题/描述。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "view-id", Type: shortcut.FlagString, Desc: "表单 View ID", Required: true},
		{Name: "title", Type: shortcut.FlagString, Desc: "表单标题(可选)"},
		{Name: "description", Type: shortcut.FlagString, Desc: "表单描述(可选)"},
	},
	Tips: []string{`dws aitable +form-update --base-id B --table-id T --view-id V --title "员工信息收集"`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
			"viewId":  rt.Str("view-id"),
		}
		if rt.Changed("title") {
			params["title"] = rt.Str("title")
		}
		if rt.Changed("description") {
			params["description"] = rt.Str("description")
		}
		return rt.CallMCP("update_form_info", params)
	},
}

FormUpdate 更新表单配置(update_form_info,server: aitable)。

View Source
var ImportData = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+import-data",
	Product:     serverMain,
	Description: "将已上传文件导入 AI 表格(新建表或追加到已有表)",
	Intent:      "当文件已上传、你要真正把它导入成新表或追加到已有表(可设表头行、指定源 Sheet、做字段映射)时使用;会实际写入数据。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "import-id", Type: shortcut.FlagString, Desc: "import upload 返回的 importId", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "追加导入的目标 Table ID(可选)"},
		{Name: "timeout", Type: shortcut.FlagInt, Desc: "等待超时(可选)"},
		{Name: "header-row", Type: shortcut.FlagInt, Desc: "表头行号(可选)"},
		{Name: "src-sheet-name", Type: shortcut.FlagString, Desc: "源 Sheet 名(可选)"},
		{Name: "field-mapping", Type: shortcut.FlagString, Desc: "字段映射 JSON 对象,key=目标字段名 value=源列名(可选)"},
	},
	Tips: []string{`dws aitable +import-data --import-id IMPORT_ID`, `dws aitable +import-data --import-id IMPORT_ID --table-id T`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{"importId": rt.Str("import-id")}
		if rt.Changed("table-id") {
			params["tableId"] = rt.Str("table-id")
		}
		if rt.Changed("timeout") {
			params["timeout"] = rt.Int("timeout")
		}
		if rt.Changed("header-row") {
			params["headerRow"] = rt.Int("header-row")
		}
		if rt.Changed("src-sheet-name") {
			params["srcSheetName"] = rt.Str("src-sheet-name")
		}
		if rt.Changed("field-mapping") {
			m, err := parseJSONObject("field-mapping", rt.Str("field-mapping"))
			if err != nil {
				return err
			}
			params["fieldMapping"] = m
		}
		return rt.CallMCP("import_data", params)
	},
}

ImportData 导入数据(import_data)。

View Source
var ImportFile = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+import-file",
	Product:     serverMain,
	Description: "申请上传、PUT 本地 CSV/XLS/XLSX,并用同一 importId 触发导入;也可只续等已有任务",
	Intent:      "当你要从本地文件完成 AI 表格导入闭环时使用;DWS 不暴露签名 URL,超时保留同一 importId 供只续等。",
	Risk:        shortcut.RiskWrite,
	Safety: contract.SafetySpec{
		Effect: "write", Risk: "medium", Confirmation: "user_required", Idempotency: "non_idempotent",
	},
	Contract: aitableCompositeContractWithResult(
		"+import-file",
		"申请上传、PUT 本地 CSV/XLS/XLSX,并用同一 importId 触发导入;也可只续等已有任务",
		"当你要从本地文件完成 AI 表格导入闭环时使用;DWS 不暴露签名 URL,超时保留同一 importId 供只续等。",
		"JSON 记录写入用 record create/upsert;只申请上传地址用 +import-upload;续等时不能改变目标表或映射参数",
		`dws aitable +import-file --base-id B --file ./data.xlsx --table-id T`,
		aitableImportFileResultSpec(),
	),
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID;首次导入模式使用,值不能为空", RequiredWhen: "未提供 --resume-import-id 时"},
		{Name: "file", Type: shortcut.FlagString, Desc: "本地非空普通 CSV/XLS/XLSX 文件;首次导入模式使用,值不能为空", RequiredWhen: "未提供 --resume-import-id 时"},
		{Name: "resume-import-id", Type: shortcut.FlagString, Desc: "只续等已有 importId;与首次导入参数互斥"},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "追加导入的目标 Table ID(可选)"},
		{Name: "timeout", Type: shortcut.FlagInt, Desc: "服务端等待超时(可选,必须大于 0)"},
		{Name: "header-row", Type: shortcut.FlagInt, Desc: "XLS/XLSX 表头行号(可选,必须大于 0;CSV 不支持)"},
		{Name: "src-sheet-name", Type: shortcut.FlagString, Desc: "XLS/XLSX 源 Sheet 名(可选;CSV 不支持)"},
		{Name: "field-mapping", Type: shortcut.FlagString, Desc: "字段映射 JSON 对象,目标字段名和源列名不能为空(可选)"},
	},
	Constraints: []shortcut.Constraint{
		{Kind: shortcut.ConstraintMutuallyExclusive, Flags: []string{"resume-import-id", "base-id"}},
		{Kind: shortcut.ConstraintMutuallyExclusive, Flags: []string{"resume-import-id", "file"}},
		{Kind: shortcut.ConstraintMutuallyExclusive, Flags: []string{"resume-import-id", "table-id"}},
		{Kind: shortcut.ConstraintMutuallyExclusive, Flags: []string{"resume-import-id", "header-row"}},
		{Kind: shortcut.ConstraintMutuallyExclusive, Flags: []string{"resume-import-id", "src-sheet-name"}},
		{Kind: shortcut.ConstraintMutuallyExclusive, Flags: []string{"resume-import-id", "field-mapping"}},
		{Kind: shortcut.ConstraintCustom, Flags: []string{"base-id", "file"}, Description: "首次导入模式下值不能为空"},
		{Kind: shortcut.ConstraintCustom, Flags: []string{"timeout", "header-row"}, Description: "显式值必须大于 0"},
		{Kind: shortcut.ConstraintCustom, Flags: []string{"field-mapping"}, Description: "目标字段名和源列名不能为空"},
	},
	Tips: []string{
		`dws aitable +import-file --base-id B --file ./data.xlsx --table-id T`,
		`dws aitable +import-file --resume-import-id IMPORT_ID --timeout 120`,
	},
	Validate: validateImportFileFlags,
	Execute: func(rt *shortcut.RuntimeContext) error {
		return executeImportFile(rt)
	},
}
View Source
var ImportUpload = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+import-upload",
	Product:     serverMain,
	Description: "为导入任务申请 OSS 直传地址(uploadUrl / importId)",
	Intent:      "当你要把本地文件(如 Excel)导入 AI 表格、需要先申请上传地址时使用;返回 uploadUrl 和 importId,供直传文件后再调用导入。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "file-name", Type: shortcut.FlagString, Desc: "文件名(含扩展名)", Required: true},
		{Name: "file-size", Type: shortcut.FlagInt, Desc: "文件大小(字节,必须大于 0)", Required: true},
	},
	Constraints: []shortcut.Constraint{
		{
			Kind:        shortcut.ConstraintCustom,
			Flags:       []string{"file-size"},
			Description: "--file-size 必须是大于 0 的整数(实际文件大小,单位字节)",
		},
	},
	Tips: []string{`dws aitable +import-upload --base-id B --file-name data.xlsx --file-size 204800`},
	Validate: func(rt *shortcut.RuntimeContext) error {
		if rt.Int("file-size") <= 0 {
			return fmt.Errorf("flag --file-size is required and must be a positive integer")
		}
		return nil
	},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":   rt.Str("base-id"),
			"fileName": rt.Str("file-name"),
			"fileSize": rt.Int("file-size"),
		}
		return rt.CallMCP("prepare_import_upload", params)
	},
}

ImportUpload 准备导入文件上传(prepare_import_upload)。

View Source
var RecordBatchCreate = shortcut.Shortcut{
	Service: "aitable", Command: "+record-batch-create", Product: serverMain,
	Description: "批量新增记录,100 条分片并按返回 ID 独立验证;已有 recordId 明确拒绝",
	Intent:      "仅新增一批记录时使用;每条 records.cells 独立取值,返回已创建 ID 和分片恢复进度。",
	Risk:        shortcut.RiskWrite,
	Safety:      contract.SafetySpec{Effect: "write", Risk: "medium", Confirmation: "user_required", Idempotency: "non_idempotent"},
	Contract:    aitableCompositeContractWithResult("+record-batch-create", "批量新增记录并逐批读回验证", "仅新增记录且需要分片与准确 ID 读回时", "更新已有行用 +record-update;有增有改用 +record-upsert", `dws aitable +record-batch-create --base-id B --table-id T --records '[{"cells":{"fldTitle":"任务"}}]'`, parityCompositeResultSpec()),
	Flags:       []shortcut.Flag{{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true}, {Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true}, {Name: "records", Type: shortcut.FlagString, Desc: "非空 records JSON 数组,每项 cells;不允许 recordId,最多 10000 项", Required: true}},
	Execute: func(rt *shortcut.RuntimeContext) error {
		records, err := parseRecordObjects(rt.Str("records"), false)
		if err != nil {
			return err
		}
		for i, r := range records {
			if _, exists := r["recordId"]; exists {
				return apperrors.NewValidation(fmt.Sprintf("records[%d] 含 recordId;新增入口不接受更新目标", i))
			}
		}
		return executeRecordBatches(rt, "record_create", "create_records", serverMain, records, verifyUpsertBatch)
	},
}
View Source
var RecordBulkPatch = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+record-bulk-patch",
	Product:     serverMain,
	Description: "完整查询目标记录后批量合并同一组 cells,自动分片并逐条读回验证",
	Intent:      "当你要按 filters/关键词/recordIds 对一批已有记录应用相同字段补丁时使用;必须显式选择范围或 --all,并受 max-matches 写前上限保护。",
	Risk:        shortcut.RiskHighWrite,
	Safety: contract.SafetySpec{
		Effect: "write", Risk: "high", Confirmation: "user_required", Idempotency: "idempotent",
	},
	Contract: aitableCompositeContract(
		"+record-bulk-patch",
		"完整查询目标记录后批量合并同一组 cells,自动分片并逐条读回验证",
		"当你要按 filters/关键词/recordIds 对一批已有记录应用相同字段补丁时使用;必须显式选择范围或 --all,并受 max-matches 写前上限保护。",
		"单条已知 recordId 用 record update;每条记录写不同值用 record update/upsert;未明确范围时不要执行",
		`dws aitable +record-bulk-patch --base-id B --table-id T --query "待处理" --patch '{"fldStatus":"完成"}'`,
	),
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "patch", Type: shortcut.FlagString, Desc: "要合并到每条记录的非空 cells JSON 对象", Required: true},
		{Name: "filters", Type: shortcut.FlagString, Desc: "query_records filters JSON(选择条件之一)"},
		{Name: "query", Type: shortcut.FlagString, Desc: "全文关键词(选择条件之一)"},
		{Name: "record-ids", Type: shortcut.FlagStringSlice, Desc: "明确的 recordId 列表(选择条件之一)"},
		{Name: "view-id", Type: shortcut.FlagString, Desc: "可选视图上下文"},
		{Name: "all", Type: shortcut.FlagBool, Desc: "明确允许匹配整张表"},
		{Name: "max-matches", Type: shortcut.FlagInt, Default: "1000", Desc: "写入前允许匹配的最大记录数,1-10000"},
	},
	Tips: []string{`dws aitable +record-bulk-patch --base-id B --table-id T --query "待处理" --patch '{"fldStatus":"完成"}'`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return executeRecordBulkPatch(rt)
	},
}
View Source
var RecordDelete = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+record-delete",
	Product:     serverMain,
	Description: "批量删除记录(不可逆),自动按 100 条分片并逐批确认记录已不存在",
	Intent:      "当你确认要批量删除若干行记录时使用;自动按 100 条分片,只有每批全部 recordId 读回均不存在才判定成功,否则非零退出并给出 nextOffset。",
	Risk:        shortcut.RiskHighWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "record-ids", Type: shortcut.FlagStringSlice, Desc: "待删除记录 ID 列表,逗号分隔;最多 10000 个唯一 ID", Required: true},
	},
	Tips:    []string{`dws aitable +record-delete --base-id B --table-id T --record-ids rec1,rec2`},
	Execute: executeRecordDeleteBatches,
}

RecordDelete 删除行记录(delete_records)。

View Source
var RecordHistoryList = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+record-history-list",
	Product:     serverHelper,
	Description: "按 recordId 查询单条记录的变更历史",
	Intent:      "当你要追溯某条记录曾被谁在何时改过哪些字段时使用;按 recordId 分页返回该行的变更历史。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_record_history_list",
			CanonicalPath:  "aitable.shortcut_record_history_list",
			CLIPath:        "aitable +record-history-list",
			PrimaryCLIPath: "aitable +record-history-list",
		},
		Description: "按 recordId 查询单条记录的变更历史",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "按 recordId 查询单条记录的变更历史",
			UseWhen:      []string{"当你要追溯某条记录曾被谁在何时改过哪些字段时使用;按 recordId 分页返回该行的变更历史。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples:     []string{"dws aitable +record-history-list --base-id B --table-id T --record-id R"},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "record-id", Type: shortcut.FlagString, Desc: "记录 ID", Required: true},
		{Name: "offset", Type: shortcut.FlagInt, Desc: "分页偏移量,默认 0(可选)"},
		{Name: "limit", Type: shortcut.FlagInt, Desc: "每页数量,默认 20,最大 50(可选)"},
	},
	Tips: []string{`dws aitable +record-history-list --base-id B --table-id T --record-id R`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":   rt.Str("base-id"),
			"tableId":  rt.Str("table-id"),
			"recordId": rt.Str("record-id"),
		}
		if rt.Changed("offset") {
			params["offset"] = rt.Int("offset")
		}
		if rt.Changed("limit") {
			params["limit"] = rt.Int("limit")
		}
		return rt.CallMCP("query_record_history", params)
	},
}

RecordHistoryList 查询记录变更历史(query_record_history,server: aitable)。

View Source
var RecordPrimaryDocCreate = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+record-primary-doc-create",
	Product:     serverMain,
	Description: "为记录创建主键文档(幂等),fieldId 须为 primaryDoc 类型",
	Intent:      "当某记录的主键文档列还没有对应文档、你要为它新建一个时使用;幂等操作,fieldId 须为 primaryDoc 类型,会实际生成主键文档。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "field-id", Type: shortcut.FlagString, Desc: "主键字段 ID(primaryDoc 类型)", Required: true},
		{Name: "record-id", Type: shortcut.FlagString, Desc: "记录 ID", Required: true},
		{Name: "doc-name", Type: shortcut.FlagString, Desc: "可选,主键文档名称"},
		{Name: "template-doc-id", Type: shortcut.FlagString, Desc: "可选,复制该模板文档内容"},
	},
	Tips: []string{`dws aitable +record-primary-doc-create --base-id B --table-id T --field-id F --record-id R`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":   rt.Str("base-id"),
			"tableId":  rt.Str("table-id"),
			"fieldId":  rt.Str("field-id"),
			"recordId": rt.Str("record-id"),
		}
		if rt.Changed("doc-name") {
			params["docName"] = rt.Str("doc-name")
		}
		if rt.Changed("template-doc-id") {
			params["templateDocId"] = rt.Str("template-doc-id")
		}
		return rt.CallMCP("create_cell_doc", params)
	},
}

RecordPrimaryDocCreate 为记录创建主键文档(create_cell_doc)。

View Source
var RecordPrimaryDocGet = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+record-primary-doc-get",
	Product:     serverMain,
	Description: "查询记录关联的主键文档 nodeId",
	Intent:      "当某记录已关联主键文档、你需要拿到该文档的 nodeId 以便打开或编辑时使用;返回主键文档 nodeId。",
	Risk:        shortcut.RiskRead,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "record-id", Type: shortcut.FlagString, Desc: "记录 ID", Required: true},
	},
	Tips: []string{`dws aitable +record-primary-doc-get --base-id B --table-id T --record-id R`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return executeRecordPrimaryDocGet(rt)
	},
}

RecordPrimaryDocGet 查询记录主键文档(get_cell_doc)。

View Source
var RecordQuery = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+record-query",
	Product:     serverMain,
	Description: recordQueryDescription,
	Intent:      recordQueryIntent,
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_record_query",
			CanonicalPath:  "aitable.shortcut_record_query",
			CLIPath:        "aitable +record-query",
			PrimaryCLIPath: "aitable +record-query",
		},
		Description: recordQueryDescription,
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: recordQueryDescription,
			UseWhen:      []string{recordQueryIntent},
			AvoidWhen:    []string{recordQueryAvoidPsql, recordQueryAvoidAll, recordQueryAvoidStats, recordQueryAvoidExport},
			Examples: []string{
				"dws aitable +record-query --base-id B --table-id T --query \"关键词\" --limit 50",
				"dws aitable +record-query --base-id B --table-id T --record-ids R1,R2 --field-ids F_NAME,F_STATUS",
			},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "record-ids", Type: shortcut.FlagStringSlice, Desc: "记录 ID 列表,单次最多 100(可选)"},
		{Name: "field-ids", Type: shortcut.FlagStringSlice, Desc: "返回字段 ID 列表(可选);必须先通过 field get 获取真实 fieldId,不要传字段中文名"},
		{Name: "filters", Type: shortcut.FlagString, Desc: "结构化过滤条件 JSON(可选);先用 field get 完整读一遍表头,确定用户条件对应的字段和类型后再传值。日期值用日期字符串或毫秒数,不接受 View relative/exact Scheme。人员、部门、群组禁止原值透传,必须分别经 aisearch person、contact +resolve-dept、chat +chat-search 唯一解析为 userId、deptId、openConversationId,再传结构化 ID 数组"},
		{Name: "sort", Type: shortcut.FlagString, Desc: "排序条件 JSON 数组(可选);fieldId 必须来自 field get,direction 仅用 asc/desc"},
		{Name: "query", Type: shortcut.FlagString, Desc: "全文关键词(可选)"},
		{Name: "limit", Type: shortcut.FlagInt, Desc: "默认单次最大记录数 100;--all 时作为每个请求的页大小,上限 20(可选)"},
		{Name: "view-id", Type: shortcut.FlagString, Desc: "准确视图 ID;读取其筛选/排序,显式 filters/sort 覆盖;与 record-ids 互斥"},
		{Name: "export-output", Type: shortcut.FlagString, Desc: "将完整结果写成 NDJSON 文件并返回哈希、行数和列信息;必须 --all,路径限工作目录内,不覆盖已有文件;全局 --output/-o 仍用于保存命令返回值"},
		{Name: "all", Type: shortcut.FlagBool, Desc: "有界读取全部匹配记录"},
		{Name: "max-records", Type: shortcut.FlagInt, Default: "10000", Desc: "--all 最多返回的记录数量,1-10000,超限明确失败"},
		{Name: "cursor", Type: shortcut.FlagString, Desc: "分页游标(可选);首次不传,后续只能原样使用上一页 data.nextCursor,并保持全部查询条件不变;普通扫描满 limit 后成功返回空续页属于正常情况,records 为空时仍以 nextCursor 是否为空判断继续或完成;不得复用旧 cursor 或自行构造。INVALID_CURSOR/CURSOR_SNAPSHOT_CHANGED 必须丢弃累计结果与旧游标,不传 --cursor 从第一页只读重查;CURSOR_SNAPSHOT_UNAVAILABLE 先等待服务修复;CURSOR_OFFSET_LIMIT 表示排序 offset 已达上限(100000),须先收窄 --filters(或改用 --record-ids/分段条件)再从第一页重查,直接重查会再次触顶。失效快照不提供续传 cursor,不按 recordId 去重拼接新旧页,也不重跑含写入的整条命令"},
	},
	Constraints: []shortcut.Constraint{
		{Kind: shortcut.ConstraintMutuallyExclusive, Flags: []string{"record-ids", "view-id"}, Description: "按准确 ID 读取与按视图查询互斥"},
		{Kind: shortcut.ConstraintMutuallyExclusive, Flags: []string{"all", "cursor"}, Description: "全量查询必须从第一页开始,不能指定续页游标"},
	},
	Tips: []string{
		`dws aitable +record-query --base-id B --table-id T --query "关键词" --limit 50`,
		`dws aitable +record-query --base-id B --table-id T --record-ids R1,R2 --field-ids F_NAME,F_STATUS`,
	},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
		}
		if rt.Changed("record-ids") {
			params["recordIds"] = rt.StrSlice("record-ids")
		}
		if rt.Changed("field-ids") {
			params["fieldIds"] = rt.StrSlice("field-ids")
		}
		if rt.Changed("filters") {
			f, err := parseRecordQueryFilters(rt.Str("filters"))
			if err != nil {
				return err
			}
			params["filters"] = f
		}
		if rt.Changed("sort") {
			s, err := parseJSONAny("sort", rt.Str("sort"))
			if err != nil {
				return err
			}
			params["sort"] = s
		}
		if rt.Changed("query") {
			params["keyword"] = rt.Str("query")
		}
		if rt.Changed("limit") {
			params["limit"] = rt.Int("limit")
		}
		if rt.Changed("cursor") {
			params["cursor"] = rt.Str("cursor")
		}
		return executeRecordQuery(rt, params)
	},
}

RecordQuery 获取行记录(query_records)。

View Source
var RecordQueryEmpty = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+record-query-empty",
	Product:     serverHelper,
	Description: "扫描并过滤出完全没填用户字段的空行",
	Intent:      "当你想清理表格、需要先找出那些所有用户字段都为空的空行时使用;扫描并返回空行列表,支持分页预算。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_record_query_empty",
			CanonicalPath:  "aitable.shortcut_record_query_empty",
			CLIPath:        "aitable +record-query-empty",
			PrimaryCLIPath: "aitable +record-query-empty",
		},
		Description: "扫描并过滤出完全没填用户字段的空行",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "扫描并过滤出完全没填用户字段的空行",
			UseWhen:      []string{"当你想清理表格、需要先找出那些所有用户字段都为空的空行时使用;扫描并返回空行列表,支持分页预算。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples:     []string{"dws aitable +record-query-empty --base-id B --table-id T"},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "limit", Type: shortcut.FlagInt, Desc: "单次扫描预算,范围 [1,100](可选)"},
		{Name: "cursor", Type: shortcut.FlagString, Desc: "分页游标(可选);首次不传,后续原样使用上一页 nextCursor;records 为空不是错误,nextCursor 非空则继续扫,为空表示已扫完全表并正常完成"},
	},
	Tips: []string{`dws aitable +record-query-empty --base-id B --table-id T`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
		}
		if rt.Changed("limit") {
			params["limit"] = rt.Int("limit")
		}
		if rt.Changed("cursor") {
			params["cursor"] = rt.Str("cursor")
		}
		return rt.CallMCP("query_empty_records", params)
	},
}

RecordQueryEmpty 查询空行(query_empty_records,server: aitable)。

View Source
var RecordShareURL = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+record-share-url",
	Product:     serverHelper,
	Description: "按 recordId 批量获取记录分享链接,单次最多 20 条",
	Intent:      "当你要把某几条记录以链接形式分享给他人(可带视图上下文)时使用;按 recordId 批量返回分享链接,单次最多 20 条。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_record_share_url",
			CanonicalPath:  "aitable.shortcut_record_share_url",
			CLIPath:        "aitable +record-share-url",
			PrimaryCLIPath: "aitable +record-share-url",
		},
		Description: "按 recordId 批量获取记录分享链接,单次最多 20 条",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "按 recordId 批量获取记录分享链接,单次最多 20 条",
			UseWhen:      []string{"当你要把某几条记录以链接形式分享给他人(可带视图上下文)时使用;按 recordId 批量返回分享链接,单次最多 20 条。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples:     []string{"dws aitable +record-share-url --base-id B --table-id T --record-ids rec1,rec2"},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "record-ids", Type: shortcut.FlagStringSlice, Desc: "记录 ID 列表,单次最多 20", Required: true},
		{Name: "view-id", Type: shortcut.FlagString, Desc: "视图 ID,生成带视图上下文的链接(可选)"},
	},
	Tips: []string{`dws aitable +record-share-url --base-id B --table-id T --record-ids rec1,rec2`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":    rt.Str("base-id"),
			"tableId":   rt.Str("table-id"),
			"recordIds": rt.StrSlice("record-ids"),
		}
		if rt.Changed("view-id") {
			params["viewId"] = rt.Str("view-id")
		}
		return rt.CallMCP("get_record_share_url", params)
	},
}

RecordShareURL 批量获取记录分享链接(get_record_share_url,server: aitable)。

View Source
var RecordUpdate = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+record-update",
	Product:     serverMain,
	Description: "批量更新记录,自动按 100 条分片并逐批读回验证",
	Intent:      "当你要批量修改已有行的字段值(每条须带 recordId)时使用;自动按 100 条分片,任一批失败或读回不一致即非零退出并给出 nextOffset。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "records", Type: shortcut.FlagString, Desc: "记录 JSON 数组,如 '[{\"recordId\":\"rec\",\"cells\":{...}}]'", Required: true},
	},
	Tips:    []string{`dws aitable +record-update --base-id B --table-id T --records '[{"recordId":"rec","cells":{"fldStatusId":"已完成"}}]'`},
	Execute: executeRecordUpdateBatches,
}

RecordCreate 新增记录(create_records)。 RecordUpdate 更新记录(update_records)。

View Source
var RecordUpsert = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+record-upsert",
	Product:     serverHelper,
	Description: "按 recordId 自动拆分 create/update,按 100 条分片并读回验证",
	Intent:      "当一批数据中部分新增、部分更新时使用;自动按 recordId 拆分并以 100 条为批次写入,创建必须返回新 recordId,全部批次都需读回验证。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "records", Type: shortcut.FlagString, Desc: "记录 JSON 数组,单次最多 100 条", Required: true},
	},
	Tips:    []string{`dws aitable +record-upsert --base-id B --table-id T --records '[{"cells":{...}}]'`},
	Execute: executeRecordUpsertBatches,
}

RecordUpsert 批量创建或更新记录(record_upsert,server: aitable)。

View Source
var RecordUpsertByKey = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+record-upsert-by-key",
	Product:     serverMain,
	Description: "按唯一字段值有则更新、无则创建记录,并读回验证",
	Intent:      "已知一个应当唯一的 fieldId/value、希望幂等同步一条记录时使用;先完整查询键值,0 条创建、1 条更新、2 条以上停止,写后再次查询并验证所有传入 cells。",
	Risk:        shortcut.RiskWrite,
	Safety: contract.SafetySpec{
		Effect: "write", Risk: "medium",
		Confirmation: "user_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_record_upsert_by_key",
			CanonicalPath:  "aitable.shortcut_record_upsert_by_key",
			CLIPath:        "aitable +record-upsert-by-key",
			PrimaryCLIPath: "aitable +record-upsert-by-key",
		},
		Description: "按唯一字段值有则更新、无则创建记录,并读回验证。",
		Interface: &contract.InterfaceSpec{
			Mode:         contract.InterfaceModeComposite,
			Availability: contract.InterfaceAvailable,
			Reason:       "The command composes query_records with create_records/update_records and a final read-back; no single RPC owns its uniqueness and verification contract.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "按唯一字段值有则更新、无则创建记录,并读回验证",
			UseWhen:      []string{"已知一个应当唯一的 fieldId/value、希望幂等同步一条记录时使用;先完整查询键值,0 条创建、1 条更新、2 条以上停止,写后再次查询并验证所有传入 cells。"},
			AvoidWhen:    []string{"已经知道 recordId 时用 +record-update;一次处理多条不同键值时使用批量导入或分批调用"},
			Examples: []string{
				"dws aitable +record-upsert-by-key --base-id B --table-id T --key-field-id fldKey --key-value TASK-001 --cells '{\"fldStatus\":\"进行中\"}'",
			},
			ExampleDispositions: []contract.ExampleDisposition{{
				Index:      recordUpsertExampleIndex(),
				Mode:       contract.ExampleDispositionModeContractOnly,
				ReasonCode: contract.ExampleDispositionReasonStatefulPreflight,
				Reason:     "dry-run must query the live table to prove whether the unique key matches zero or one record; the isolated Agent example runner has no remote AITable fixture",
				Reviewed:   true,
			}},
		},
		DryRun: &contract.DryRunSpec{PreviewKind: "plan", RemoteReads: true},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "key-field-id", Type: shortcut.FlagString, Desc: "具有唯一语义的字段 ID", Required: true},
		{Name: "key-value", Type: shortcut.FlagString, Desc: "字符串键值;与 --key-value-json 二选一"},
		{Name: "key-value-json", Type: shortcut.FlagString, Desc: "JSON 类型键值;与 --key-value 二选一"},
		{Name: "cells", Type: shortcut.FlagString, Desc: "要写入的 cells JSON 对象", Required: true},
	},
	Constraints: []shortcut.Constraint{
		{Kind: shortcut.ConstraintExactlyOne, Flags: []string{"key-value", "key-value-json"}, Description: "必须且只能提供一种键值表示"},
	},
	Tips: []string{
		`dws aitable +record-upsert-by-key --base-id B --table-id T --key-field-id fldKey --key-value TASK-001 --cells '{"fldStatus":"进行中"}'`,
	},
	Execute: executeRecordUpsertByKey,
}

RecordUpsertByKey creates or updates exactly one record selected by a unique field value. It never writes when the key is ambiguous and always verifies the final record through a read-back query.

View Source
var RecordWriteResult = shortcut.Shortcut{
	OutputRollout: output.RolloutUnifiedActive,
	Service:       "aitable", Command: "+record-write-result", Product: serverMain,
	Description: "按原 clientToken 只读核对已落库记录;未知结果不能作为重新创建依据",
	Intent:      "批量写入失败或超时且持有原 clientToken 时使用;只读核对实际已应用的记录 ID,不补写或重放,未知结果不代表未写入。unknown 或 ID 不完整时停止后续写入,下一步仍使用本命令和原 baseId/tableId/clientToken 只读对账,不能用全表查询替代;完整 ID 集合确认后才逐 ID 核对实际值。返回部分 ID 只证明这些记录已应用,不证明其余记录未写入,不能按数量差额补写或更换 token。用户评审恢复方案并要求下一步命令时,先给本命令使用原三个参数的完整只读命令并注明未执行,再解释判断;不要先展开 Schema 字段表。",
	Risk:        shortcut.RiskRead,
	Safety:      contract.SafetySpec{Effect: "read", Risk: "low", Confirmation: "not_required", Idempotency: "idempotent"},
	Contract: aitableCompositeContractWithResult("+record-write-result", "只读核对一次记录写入的实际结果",
		"批量写入失败或超时后,持有原 clientToken 需要确定已写入 ID 时", "普通查询用 +record-query;本命令不会补写或重放数据",
		`dws aitable +record-write-result --base-id B --table-id T --client-token 123e4567-e89b-42d3-a456-426614174000`,
		&contract.ResultSpec{Outcomes: []contract.ResultOutcome{contract.ResultOutcomeSuccess, contract.ResultOutcomeFailure}, DataSchema: json.RawMessage(recordWriteResultSchema)}),
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "原写入 Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "原写入 Table ID", Required: true},
		{Name: "client-token", Type: shortcut.FlagString, Desc: "原写入使用的 UUID v4", Required: true},
	},
	Execute: func(rt *shortcut.RuntimeContext) error {
		if err := aitableprotocol.ValidateClientToken(rt.Str("client-token")); err != nil {
			return err
		}
		args := map[string]any{"baseId": rt.Str("base-id"), "tableId": rt.Str("table-id"), "clientToken": rt.Str("client-token")}
		if rt.DryRun() {
			return rt.Output(map[string]any{"executed": false, "arguments": args})
		}
		data, err := rt.CallMCPData(serverMain, "get_record_write_result", args)
		if err != nil {
			return err
		}
		body := parityResponseObject(data)
		if _, err := reconciledRecordIDs(body, rt.Str("base-id"), rt.Str("table-id"), rt.Str("client-token")); err != nil {
			return err
		}
		return rt.Output(body)
	},
}
View Source
var RoleCreate = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+role-create",
	Product:     serverHelper,
	Description: "在指定 Base 下创建自定义角色",
	Intent:      "当你要在已开启高级权限的 Base 下新建一个自定义角色并设定其对各表的读写权限时使用;会实际创建角色。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "name", Type: shortcut.FlagString, Desc: "角色名称", Required: true},
		{Name: "role-type", Type: shortcut.FlagString, Desc: "角色类型(可选)"},
		{Name: "flow-type", Type: shortcut.FlagString, Desc: "流转类型(可选)"},
		{Name: "sub-roles", Type: shortcut.FlagString, Desc: "子角色 JSON 数组(可选)"},
	},
	Tips: []string{`dws aitable +role-create --base-id B --name "市场可读" --sub-roles '[{"targetId":"tbl","targetType":"sheet","authLevel":"read"}]'`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId": rt.Str("base-id"),
			"name":   rt.Str("name"),
		}
		if rt.Changed("role-type") {
			params["roleType"] = rt.Str("role-type")
		}
		if rt.Changed("flow-type") {
			params["flowType"] = rt.Str("flow-type")
		}
		if rt.Changed("sub-roles") {
			sr, err := parseJSONAny("sub-roles", rt.Str("sub-roles"))
			if err != nil {
				return err
			}
			params["subRoles"] = sr
		}
		return rt.CallMCP("create_role", params)
	},
}

RoleCreate 创建自定义角色(create_role)。

View Source
var RoleDelete = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+role-delete",
	Product:     serverHelper,
	Description: "删除 Base 下指定的自定义角色(不可逆)",
	Intent:      "当你确认要删除某个自定义角色时使用;不可逆,删除后被授予该角色的成员将失去对应权限。",
	Risk:        shortcut.RiskHighWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "role-id", Type: shortcut.FlagString, Desc: "Role ID(数字 long 字符串)", Required: true},
	},
	Tips: []string{`dws aitable +role-delete --base-id B --role-id R`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("delete_role", map[string]any{
			"baseId": rt.Str("base-id"),
			"roleId": rt.Str("role-id"),
		})
	},
}

RoleDelete 删除自定义角色(delete_role)。

View Source
var RoleGet = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+role-get",
	Product:     serverHelper,
	Description: "获取单个角色的完整配置",
	Intent:      "当你要查看某个角色的完整权限配置(含各表/字段的授权级别)时使用;返回单个角色的详情。",
	Risk:        shortcut.RiskRead,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "role-id", Type: shortcut.FlagString, Desc: "Role ID", Required: true},
	},
	Tips: []string{`dws aitable +role-get --base-id B --role-id R`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("get_role", map[string]any{
			"baseId": rt.Str("base-id"),
			"roleId": rt.Str("role-id"),
		})
	},
}

RoleGet 获取单个角色配置(get_role)。

View Source
var RoleList = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+role-list",
	Product:     serverHelper,
	Description: "列出指定 Base 下的全部角色",
	Intent:      "当你要查看某 Base 下配置了哪些角色(拿 roleId)时使用;返回全部角色列表。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_role_list",
			CanonicalPath:  "aitable.shortcut_role_list",
			CLIPath:        "aitable +role-list",
			PrimaryCLIPath: "aitable +role-list",
		},
		Description: "列出指定 Base 下的全部角色",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "列出指定 Base 下的全部角色",
			UseWhen:      []string{"当你要查看某 Base 下配置了哪些角色(拿 roleId)时使用;返回全部角色列表。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples:     []string{"dws aitable +role-list --base-id B"},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
	},
	Tips: []string{`dws aitable +role-list --base-id B`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("list_roles", map[string]any{"baseId": rt.Str("base-id")})
	},
}

RoleList 列出 Base 下所有角色(list_roles)。

View Source
var RoleUpdate = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+role-update",
	Product:     serverHelper,
	Description: "按 PATCH 语义增量更新自定义角色",
	Intent:      "当你要按需增量调整某自定义角色的名称或子权限(PATCH 语义,只改传入项)时使用;会实际更新该角色。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "role-id", Type: shortcut.FlagString, Desc: "Role ID", Required: true},
		{Name: "name", Type: shortcut.FlagString, Desc: "新角色名(可选)"},
		{Name: "role-type", Type: shortcut.FlagString, Desc: "角色类型(可选)"},
		{Name: "flow-type", Type: shortcut.FlagString, Desc: "流转类型(可选)"},
		{Name: "sub-roles", Type: shortcut.FlagString, Desc: "子角色 JSON 数组(可选)"},
	},
	Tips: []string{`dws aitable +role-update --base-id B --role-id R --name "新名字"`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId": rt.Str("base-id"),
			"roleId": rt.Str("role-id"),
		}
		if rt.Changed("name") {
			params["name"] = rt.Str("name")
		}
		if rt.Changed("role-type") {
			params["roleType"] = rt.Str("role-type")
		}
		if rt.Changed("flow-type") {
			params["flowType"] = rt.Str("flow-type")
		}
		if rt.Changed("sub-roles") {
			sr, err := parseJSONAny("sub-roles", rt.Str("sub-roles"))
			if err != nil {
				return err
			}
			params["subRoles"] = sr
		}
		return rt.CallMCP("patch_role", params)
	},
}

RoleUpdate 增量更新自定义角色(patch_role)。

View Source
var SectionCreate = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+section-create",
	Product:     serverHelper,
	Description: "在指定 Base 下创建文件夹(组织 table / dashboard)",
	Intent:      "当你要在 Base 内新建文件夹来归类数据表和仪表盘时使用;会实际创建文件夹,可指定父文件夹和插入位置。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "name", Type: shortcut.FlagString, Desc: "文件夹名称", Required: true},
		{Name: "parent-section-id", Type: shortcut.FlagString, Desc: "父文件夹 ID,空表示根目录(可选)"},
		{Name: "index", Type: shortcut.FlagInt, Default: "-1", Desc: "0-based 位置,不传追加到末尾(可选)"},
	},
	Tips: []string{`dws aitable +section-create --base-id B --name 我的文件夹`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId": rt.Str("base-id"),
			"name":   rt.Str("name"),
		}
		if rt.Changed("parent-section-id") {
			params["parentSectionId"] = rt.Str("parent-section-id")
		}
		if idx := rt.Int("index"); idx >= 0 {
			params["index"] = idx
		}
		return rt.CallMCP("create_section", params)
	},
}

SectionCreate 创建文件夹(create_section)。

View Source
var SectionDelete = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+section-delete",
	Product:     serverHelper,
	Description: "删除指定文件夹(不可逆)",
	Intent:      "当你确认要删除 Base 内某个文件夹时使用;不可逆,操作前请确认其内节点的处置。",
	Risk:        shortcut.RiskHighWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "section-id", Type: shortcut.FlagString, Desc: "Section ID", Required: true},
	},
	Tips: []string{`dws aitable +section-delete --base-id B --section-id S`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("delete_section", map[string]any{
			"baseId":    rt.Str("base-id"),
			"sectionId": rt.Str("section-id"),
		})
	},
}

SectionDelete 删除文件夹(delete_section)。

View Source
var SectionListEmpty = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+section-list-empty",
	Product:     serverHelper,
	Description: "列出指定 Base 下所有没有子节点的空文件夹",
	Intent:      "当你想清理 Base、需要先找出所有没有任何子节点的空文件夹时使用;返回空文件夹列表。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_section_list_empty",
			CanonicalPath:  "aitable.shortcut_section_list_empty",
			CLIPath:        "aitable +section-list-empty",
			PrimaryCLIPath: "aitable +section-list-empty",
		},
		Description: "列出指定 Base 下所有没有子节点的空文件夹",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "列出指定 Base 下所有没有子节点的空文件夹",
			UseWhen:      []string{"当你想清理 Base、需要先找出所有没有任何子节点的空文件夹时使用;返回空文件夹列表。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples:     []string{"dws aitable +section-list-empty --base-id B"},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
	},
	Tips: []string{`dws aitable +section-list-empty --base-id B`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("list_empty_sections", map[string]any{"baseId": rt.Str("base-id")})
	},
}

SectionListEmpty 列出空文件夹(list_empty_sections)。

View Source
var SectionListNodes = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+section-list-nodes",
	Product:     serverHelper,
	Description: "列出指定 Base 当前版本下的全部 nsheet 节点",
	Intent:      "当你要总览某 Base 当前版本下的全部节点(表、仪表盘、文件夹等)目录结构时使用;返回所有 nsheet 节点。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_section_list_nodes",
			CanonicalPath:  "aitable.shortcut_section_list_nodes",
			CLIPath:        "aitable +section-list-nodes",
			PrimaryCLIPath: "aitable +section-list-nodes",
		},
		Description: "列出指定 Base 当前版本下的全部 nsheet 节点",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "列出指定 Base 当前版本下的全部 nsheet 节点",
			UseWhen:      []string{"当你要总览某 Base 当前版本下的全部节点(表、仪表盘、文件夹等)目录结构时使用;返回所有 nsheet 节点。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples:     []string{"dws aitable +section-list-nodes --base-id B"},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "type", Type: shortcut.FlagString, Desc: "仅返回指定 nodeType;使用目录实际返回的类型值"},
		{Name: "parent-id", Type: shortcut.FlagString, Desc: "仅返回该父分区直接子项;显式空值筛根目录"},
	},
	Tips: []string{`dws aitable +section-list-nodes --base-id B`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		if rt.Changed("type") || rt.Changed("parent-id") {
			return outputFilteredBaseNodes(rt)
		}
		return rt.CallMCP("list_nsheet_nodes", map[string]any{"baseId": rt.Str("base-id")})
	},
}

SectionListNodes 列出全部节点(list_nsheet_nodes)。

View Source
var SectionMoveNode = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+section-move-node",
	Product:     serverHelper,
	Description: "把任意 nsheet 节点移动到目标文件夹下(可选调整位置)",
	Intent:      "当你要把某张表/仪表盘/文件夹移动到另一个文件夹下、或移到 Base 根目录(可选调整位置)时使用;会实际改变节点在目录树中的位置。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "node-id", Type: shortcut.FlagString, Desc: "待移动节点 ID", Required: true},
		{Name: "new-parent-section-id", Type: shortcut.FlagString, Desc: "目标父文件夹 ID,空字符串表示 Base 根目录"},
		{Name: "target-index", Type: shortcut.FlagInt, Default: "-1", Desc: "0-based 全局下标(可选)"},
	},
	Tips: []string{`dws aitable +section-move-node --base-id B --node-id N --new-parent-section-id S`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":             rt.Str("base-id"),
			"nodeId":             rt.Str("node-id"),
			"newParentSectionId": rt.Str("new-parent-section-id"),
		}
		if idx := rt.Int("target-index"); idx >= 0 {
			params["targetIndex"] = idx
		}
		return rt.CallMCP("move_nsheet_node", params)
	},
}

SectionMoveNode 移动节点(move_nsheet_node)。

View Source
var SectionRename = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+section-rename",
	Product:     serverHelper,
	Description: "重命名指定文件夹",
	Intent:      "当你要给 Base 内某个文件夹改名时使用;会实际重命名该 section。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "section-id", Type: shortcut.FlagString, Desc: "Section ID", Required: true},
		{Name: "new-name", Type: shortcut.FlagString, Desc: "新名称", Required: true},
	},
	Tips: []string{`dws aitable +section-rename --base-id B --section-id S --new-name 新名称`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("rename_section", map[string]any{
			"baseId":    rt.Str("base-id"),
			"sectionId": rt.Str("section-id"),
			"newName":   rt.Str("new-name"),
		})
	},
}

SectionRename 重命名文件夹(rename_section)。

View Source
var SectionReorder = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+section-reorder",
	Product:     serverHelper,
	Description: "在当前父文件夹下调整文件夹的展示顺序",
	Intent:      "当你要在同一父文件夹下调整某文件夹的排列先后顺序时使用;会实际把它移动到指定的 0-based 位置。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "section-id", Type: shortcut.FlagString, Desc: "Section ID", Required: true},
		{Name: "target-index", Type: shortcut.FlagInt, Desc: "0-based 目标位置", Required: true},
	},
	Tips: []string{`dws aitable +section-reorder --base-id B --section-id S --target-index 0`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("reorder_section", map[string]any{
			"baseId":      rt.Str("base-id"),
			"sectionId":   rt.Str("section-id"),
			"targetIndex": rt.Int("target-index"),
		})
	},
}

SectionReorder 调整文件夹顺序(reorder_section)。

View Source
var TableBootstrap = shortcut.Shortcut{
	OutputRollout: output.RolloutUnifiedActive,
	Service:       "aitable",
	Command:       "+table-bootstrap",
	Product:       serverMain,
	Description:   "在已有 Base 中一次创建数据表和字段,自动分片并读回验证",
	Intent:        "当你已有 baseId、需要新增一张带完整字段结构的数据表时使用;替代 table create 后连续 field create 和手工验证。",
	Risk:          shortcut.RiskWrite,
	Safety: contract.SafetySpec{
		Effect: "write", Risk: "medium", Confirmation: "user_required", Idempotency: "non_idempotent",
	},
	Contract: aitableCompositeContractWithResult(
		"+table-bootstrap",
		"在已有 Base 中一次创建数据表和字段,自动分片并读回验证",
		"当你已有 baseId、需要新增一张带完整字段结构的数据表时使用;替代 table create 后连续 field create 和手工验证。",
		"需要同时新建 Base 用 +base-bootstrap;复制现有表用 +table-copy;只补字段用 field create",
		`dws aitable +table-bootstrap --base-id BASE_ID --name "任务" --fields '[{"fieldName":"标题","type":"text"}]'`,
		aitableTableBootstrapResultSpec(),
	),
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "目标 Base ID", Required: true},
		{Name: "name", Type: shortcut.FlagString, Desc: "新数据表名称", Required: true},
		{Name: "fields", Type: shortcut.FlagString, Desc: "字段结构 JSON 数组;字段对象使用 fieldName/type/config,可选 description/aiConfig", Required: true},
	},
	Tips: []string{
		`dws aitable +table-bootstrap --base-id BASE_ID --name "任务" --fields '[{"fieldName":"标题","type":"text"}]'`,
	},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return executeTableBootstrap(rt)
	},
}
View Source
var TableCopy = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+table-copy",
	Product:     serverMain,
	Description: "跨 Base 同步复制一张表的可创建字段结构,并可同步复制全部记录",
	Intent:      "当服务端没有 table copy/task 接口、但你需要在目标 Base 重建一张表时使用;本地编排字段分片、fieldId 映射、记录分片和读回验证。",
	Risk:        shortcut.RiskWrite,
	Safety: contract.SafetySpec{
		Effect: "write", Risk: "medium", Confirmation: "user_required", Idempotency: "non_idempotent",
	},
	Contract: aitableCompositeContract(
		"+table-copy",
		"跨 Base 同步复制一张表的可创建字段结构,并可同步复制全部记录",
		"当服务端没有 table copy/task 接口、但你需要在目标 Base 重建一张表时使用;本地编排字段分片、fieldId 映射、记录分片和读回验证。",
		"复制整个 Base 用 base copy;需要复制公式、查找引用、关联字段或所有视图时先用 schema snapshot 手工处理这些无法安全重映射的依赖",
		`dws aitable +table-copy --source-base-id B1 --source-table-id T1 --target-base-id B2 --new-name "任务副本"`,
	),
	Flags: []shortcut.Flag{
		{Name: "source-base-id", Type: shortcut.FlagString, Desc: "源 Base ID", Required: true},
		{Name: "source-table-id", Type: shortcut.FlagString, Desc: "源 Table ID", Required: true},
		{Name: "target-base-id", Type: shortcut.FlagString, Desc: "目标 Base ID", Required: true},
		{Name: "new-name", Type: shortcut.FlagString, Desc: "目标表名", Required: true},
		{Name: "strict-fields", Type: shortcut.FlagBool, Desc: "发现公式/关联/查找/系统等无法重建字段时在写入前失败;不代表复制所有视图"},
		{Name: "include-records", Type: shortcut.FlagBool, Desc: "复制全部记录;默认只复制可安全重建的字段结构"},
		{Name: "max-records", Type: shortcut.FlagInt, Default: "10000", Desc: "复制记录的写前上限,1-10000"},
	},
	Tips: []string{`dws aitable +table-copy --source-base-id B1 --source-table-id T1 --target-base-id B2 --new-name "任务副本"`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return executeTableCopy(rt)
	},
}
View Source
var TableDelete = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+table-delete",
	Product:     serverMain,
	Description: "删除指定数据表(不可逆)",
	Intent:      "当你确认要删除整张数据表(连同其所有记录和视图)时使用;不可逆,操作前请核对 tableId。",
	Risk:        shortcut.RiskHighWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "待删除 Table ID", Required: true},
		{Name: "reason", Type: shortcut.FlagString, Desc: "删除原因(可选)"},
	},
	Tips: []string{`dws aitable +table-delete --base-id B --table-id T`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
			"confirm": true,
		}
		if rt.Changed("reason") {
			params["reason"] = rt.Str("reason")
		}
		return rt.CallMCP("delete_table", params)
	},
}

TableDelete 删除数据表(delete_table)。

View Source
var TableGet = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+table-get",
	Product:     serverMain,
	Description: "批量获取指定数据表的表级信息、字段目录与视图目录",
	Intent:      "当你已进入某个 Base、需要了解其中某些数据表有哪些字段(拿 fieldId)、有哪些视图(拿 viewId)以便读写数据时使用;批量返回表信息、字段目录和视图目录。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_table_get",
			CanonicalPath:  "aitable.shortcut_table_get",
			CLIPath:        "aitable +table-get",
			PrimaryCLIPath: "aitable +table-get",
		},
		Description: "批量获取指定数据表的表级信息、字段目录与视图目录",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "批量获取指定数据表的表级信息、字段目录与视图目录",
			UseWhen:      []string{"当你已进入某个 Base、需要了解其中某些数据表有哪些字段(拿 fieldId)、有哪些视图(拿 viewId)以便读写数据时使用;批量返回表信息、字段目录和视图目录。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples: []string{
				"dws aitable +table-get --base-id BASE_ID",
				"dws aitable +table-get --base-id B --table-ids tbl1,tbl2",
			},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-ids", Type: shortcut.FlagStringSlice, Desc: "Table ID 列表,逗号分隔,单次最多 10 个(可选)"},
	},
	Tips: []string{`dws aitable +table-get --base-id BASE_ID`, `dws aitable +table-get --base-id B --table-ids tbl1,tbl2`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{"baseId": rt.Str("base-id")}
		if rt.Changed("table-ids") {
			params["tableIds"] = rt.StrSlice("table-ids")
		}
		return rt.CallMCP("get_tables", params)
	},
}

TableGet 获取数据表(get_tables)。

View Source
var TableUpdate = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+table-update",
	Product:     serverMain,
	Description: "更新数据表名称 / 备注 / 行命名规则",
	Intent:      "当你要给数据表改名、改备注,或调整行的命名规则(如按 task/project 命名)时使用;会实际更新表级属性。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "name", Type: shortcut.FlagString, Desc: "新表名(可选)"},
		{Name: "description", Type: shortcut.FlagString, Desc: "备注说明(可选)"},
		{Name: "record-name-key", Type: shortcut.FlagString, Desc: "行命名规则枚举键,如 task/project(可选)"},
	},
	Tips: []string{`dws aitable +table-update --base-id B --table-id T --name "新表名"`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
		}
		if rt.Changed("name") {
			params["newTableName"] = rt.Str("name")
		}
		if rt.Changed("description") {
			params["description"] = rt.Str("description")
		}
		if rt.Changed("record-name-key") {
			params["recordNameKey"] = rt.Str("record-name-key")
		}
		return rt.CallMCP("update_table", params)
	},
}

TableCreate 创建数据表(create_table)。 TableUpdate 更新数据表(update_table)。

View Source
var TemplateSearch = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+template-search",
	Product:     serverMain,
	Description: "按名称关键词搜索 AI 表格模板",
	Intent:      "当你要新建表格并想套用现成模板、需要先按关键词找模板(不传关键词则返回热门)时使用;返回模板列表及其模板 ID。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_template_search",
			CanonicalPath:  "aitable.shortcut_template_search",
			CLIPath:        "aitable +template-search",
			PrimaryCLIPath: "aitable +template-search",
		},
		Description: "按名称关键词搜索 AI 表格模板",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "按名称关键词搜索 AI 表格模板",
			UseWhen:      []string{"当你要新建表格并想套用现成模板、需要先按关键词找模板(不传关键词则返回热门)时使用;返回模板列表及其模板 ID。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples:     []string{"dws aitable +template-search --query \"项目管理\""},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "query", Type: shortcut.FlagString, Desc: "模板名称关键词(可选,不传返回热门)"},
		{Name: "limit", Type: shortcut.FlagInt, Desc: "每页数量,默认 10,最大 30(可选)"},
		{Name: "cursor", Type: shortcut.FlagString, Desc: "分页游标(可选)"},
	},
	Tips: []string{`dws aitable +template-search --query "项目管理"`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{}
		if rt.Changed("query") {
			params["query"] = rt.Str("query")
		}
		if rt.Changed("limit") {
			params["limit"] = rt.Int("limit")
		}
		if rt.Changed("cursor") {
			params["cursor"] = rt.Str("cursor")
		}
		data, err := rt.CallMCPData(serverMain, "search_templates", params)
		if err != nil {
			return err
		}
		templates, err := templateSearchProject(data)
		if err != nil {
			return err
		}
		return rt.Output(map[string]any{"count": len(templates), "templates": templates})
	},
}

TemplateSearch 搜索模板(search_templates)。

View Source
var URLResolve = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+url-resolve",
	Product:     serverMain,
	Description: "解析 AI 表格 URL 中的 baseId/tableId/viewId/recordId",
	Intent:      "已有钉钉 AI 表格链接、需要提取后续命令使用的稳定 ID 时使用;默认只做严格本地解析,--verify 会调用对应只读接口确认最深层目标真实存在。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_url_resolve",
			CanonicalPath:  "aitable.shortcut_url_resolve",
			CLIPath:        "aitable +url-resolve",
			PrimaryCLIPath: "aitable +url-resolve",
		},
		Description: "解析 AI 表格 URL 中的 baseId/tableId/viewId/recordId。",
		Interface: &contract.InterfaceSpec{
			Mode:         contract.InterfaceModeComposite,
			Availability: contract.InterfaceAvailable,
			Reason:       "The command performs strict local URL decoding and can optionally verify the deepest target through a read-only AI Table interface.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "解析 AI 表格 URL 中的 baseId/tableId/viewId/recordId",
			UseWhen:      []string{"已有钉钉 AI 表格链接、需要提取后续命令使用的稳定 ID 时使用;默认只做严格本地解析,--verify 会调用对应只读接口确认最深层目标真实存在。"},
			AvoidWhen:    []string{"只有资源名称而没有 URL 时改用 +resolve-base / +resolve-table;普通文档 URL 用 doc/drive"},
			Examples: []string{
				"dws aitable +url-resolve --url https://alidocs.dingtalk.com/i/nodes/BASE_ID",
				"dws aitable +url-resolve --url 'https://alidocs.dingtalk.com/i/nodes/BASE_ID?iframeQuery=sheetId%3DTABLE_ID%26viewId%3DVIEW_ID' --verify",
			},
		},
		Parameters: []contract.ParamDecl{
			{Name: "url", Description: "钉钉 AI 表格节点 URL"},
			{Name: "verify", Description: "通过只读接口验证最深层目标存在"},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "url", Type: shortcut.FlagString, Desc: "AI 表格 URL", Required: true},
		{Name: "verify", Type: shortcut.FlagBool, Default: "false", Desc: "调用只读接口验证最深层目标存在"},
	},
	Tips: []string{
		`dws aitable +url-resolve --url "https://alidocs.dingtalk.com/i/nodes/BASE_ID?iframeQuery=sheetId%3DTABLE_ID"`,
	},
	Execute: func(rt *shortcut.RuntimeContext) error {
		target, err := aitabletarget.ParseURL(rt.Str("url"))
		if err != nil {
			return err
		}
		output := map[string]any{
			"status":             "resolved",
			"target":             target,
			"verified":           false,
			"verificationStatus": "not_requested",
		}
		if rt.Bool("verify") {
			verification, err := verifyURLTarget(rt, target)
			if err != nil {
				return err
			}
			output["verified"] = true
			output["verificationStatus"] = "verified"
			output["verification"] = verification
		}
		return rt.Output(output)
	},
}

URLResolve parses a documented DingTalk AI Table URL into stable target IDs.

View Source
var ViewDelete = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+view-delete",
	Product:     serverMain,
	Description: "删除指定视图(不可逆)",
	Intent:      "当你确认要删除某个视图时使用;不可逆,仅删除该展示视图、不影响底层记录数据。",
	Risk:        shortcut.RiskHighWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "view-id", Type: shortcut.FlagString, Desc: "待删除 View ID", Required: true},
	},
	Tips: []string{`dws aitable +view-delete --base-id B --table-id T --view-id V`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("delete_view", map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
			"viewId":  rt.Str("view-id"),
			"confirm": true,
		})
	},
}

ViewDelete 删除视图(delete_view)。

View Source
var ViewDuplicate = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+view-duplicate",
	Product:     serverHelper,
	Description: "复制视图,生成配置相同的新视图",
	Intent:      "当你想基于某个已配置好的视图快速再造一个相同配置的视图时使用;会实际复制生成新视图。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "view-id", Type: shortcut.FlagString, Desc: "源 View ID", Required: true},
		{Name: "new-name", Type: shortcut.FlagString, Desc: "新视图名(可选)"},
	},
	Tips: []string{`dws aitable +view-duplicate --base-id B --table-id T --view-id V`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":       rt.Str("base-id"),
			"tableId":      rt.Str("table-id"),
			"sourceViewId": rt.Str("view-id"),
		}
		if rt.Changed("new-name") {
			params["newViewName"] = rt.Str("new-name")
		}
		return rt.CallMCP("duplicate_view", params)
	},
}

ViewDuplicate 复制视图(duplicate_view,server: aitable)。

View Source
var ViewGet = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+view-get",
	Product:     serverMain,
	Description: "获取视图完整信息(列顺序、筛选、排序、分组等)",
	Intent:      "当你要了解某个视图当前的列顺序、筛选、排序、分组等完整配置以便复用或修改时使用;批量返回视图详情。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_view_get",
			CanonicalPath:  "aitable.shortcut_view_get",
			CLIPath:        "aitable +view-get",
			PrimaryCLIPath: "aitable +view-get",
		},
		Description: "获取视图完整信息(列顺序、筛选、排序、分组等)",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "获取视图完整信息(列顺序、筛选、排序、分组等)",
			UseWhen:      []string{"当你要了解某个视图当前的列顺序、筛选、排序、分组等完整配置以便复用或修改时使用;批量返回视图详情。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples:     []string{"dws aitable +view-get --base-id B --table-id T"},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "view-ids", Type: shortcut.FlagStringSlice, Desc: "View ID 列表,单次最多 10 个(可选)"},
	},
	Tips: []string{`dws aitable +view-get --base-id B --table-id T`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
		}
		if rt.Changed("view-ids") {
			params["viewIds"] = rt.StrSlice("view-ids")
		}
		data, err := rt.CallMCPData(serverMain, "get_views", params)
		if err != nil {
			return err
		}
		views, err := viewGetProject(data)
		if err != nil {
			return err
		}
		return rt.Output(map[string]any{"count": len(views), "views": views})
	},
}

ViewGet 获取视图详情(get_views)。

View Source
var ViewGetFrozenCols = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+view-get-frozen-cols",
	Product:     serverHelper,
	Description: "获取视图当前冻结的左侧列数",
	Intent:      "当你想知道某视图当前冻结了左侧几列时使用;返回冻结列数。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_view_get_frozen_cols",
			CanonicalPath:  "aitable.shortcut_view_get_frozen_cols",
			CLIPath:        "aitable +view-get-frozen-cols",
			PrimaryCLIPath: "aitable +view-get-frozen-cols",
		},
		Description: "获取视图当前冻结的左侧列数",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "获取视图当前冻结的左侧列数",
			UseWhen:      []string{"当你想知道某视图当前冻结了左侧几列时使用;返回冻结列数。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples:     []string{"dws aitable +view-get-frozen-cols --base-id B --table-id T --view-id V"},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "view-id", Type: shortcut.FlagString, Desc: "View ID", Required: true},
	},
	Tips: []string{`dws aitable +view-get-frozen-cols --base-id B --table-id T --view-id V`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("get_frozen_columns_of_view", map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
			"viewId":  rt.Str("view-id"),
		})
	},
}

ViewGetFrozenCols 获取视图冻结列数(get_frozen_columns_of_view,server: aitable)。

View Source
var ViewGetLock = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+view-get-lock",
	Product:     serverHelper,
	Description: "获取视图锁定状态",
	Intent:      "当你想确认某视图是否已被锁定(以防他人误改其配置)时使用;返回该视图的锁定状态。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_view_get_lock",
			CanonicalPath:  "aitable.shortcut_view_get_lock",
			CLIPath:        "aitable +view-get-lock",
			PrimaryCLIPath: "aitable +view-get-lock",
		},
		Description: "获取视图锁定状态",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "获取视图锁定状态",
			UseWhen:      []string{"当你想确认某视图是否已被锁定(以防他人误改其配置)时使用;返回该视图的锁定状态。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples:     []string{"dws aitable +view-get-lock --base-id B --table-id T --view-id V"},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "view-id", Type: shortcut.FlagString, Desc: "View ID", Required: true},
	},
	Tips: []string{`dws aitable +view-get-lock --base-id B --table-id T --view-id V`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("get_view_lock_status", map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
			"viewId":  rt.Str("view-id"),
		})
	},
}

ViewGetLock 获取视图锁定状态(get_view_lock_status,server: aitable)。

View Source
var ViewGetRowHeight = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+view-get-row-height",
	Product:     serverHelper,
	Description: "获取视图单元格行高(像素)",
	Intent:      "当你想知道某视图当前的行高档位时使用;返回单元格行高的像素值。",
	Risk:        shortcut.RiskRead,
	Safety: contract.SafetySpec{
		Effect: "read", Risk: "low",
		Confirmation: "not_required", Idempotency: "idempotent",
	},
	Contract: corecmd.ContractDecl{
		Identity: contract.ToolIdentitySpec{
			ProductID:      "aitable",
			Name:           "shortcut_view_get_row_height",
			CanonicalPath:  "aitable.shortcut_view_get_row_height",
			CLIPath:        "aitable +view-get-row-height",
			PrimaryCLIPath: "aitable +view-get-row-height",
		},
		Description: "获取视图单元格行高(像素)",
		Interface: &contract.InterfaceSpec{
			Mode:         "composite",
			Availability: "available",
			Reason:       "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
		},
		Selection: contract.SelectionSpec{
			AgentSummary: "获取视图单元格行高(像素)",
			UseWhen:      []string{"当你想知道某视图当前的行高档位时使用;返回单元格行高的像素值。"},
			AvoidWhen:    []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
			Examples:     []string{"dws aitable +view-get-row-height --base-id B --table-id T --view-id V"},
		},
	},
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "view-id", Type: shortcut.FlagString, Desc: "View ID", Required: true},
	},
	Tips: []string{`dws aitable +view-get-row-height --base-id B --table-id T --view-id V`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("get_cell_height_of_view", map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
			"viewId":  rt.Str("view-id"),
		})
	},
}

ViewGetRowHeight 获取视图行高(get_cell_height_of_view,server: aitable)。

View Source
var ViewLock = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+view-lock",
	Product:     serverHelper,
	Description: "锁定视图(默认)或解锁(--off)",
	Intent:      "当你要锁定视图以防止他人修改其配置、或反过来解锁时使用;默认锁定,加 --off 解锁,会实际改变视图锁定状态。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "view-id", Type: shortcut.FlagString, Desc: "View ID", Required: true},
		{Name: "off", Type: shortcut.FlagBool, Desc: "传入则解锁(unlock),默认锁定(lock)"},
	},
	Tips: []string{`dws aitable +view-lock --base-id B --table-id T --view-id V`, `dws aitable +view-lock --base-id B --table-id T --view-id V --off`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		action := "lock"
		if rt.Bool("off") {
			action = "unlock"
		}
		return rt.CallMCP("lock_or_unlock_view", map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
			"viewId":  rt.Str("view-id"),
			"action":  action,
		})
	},
}

ViewLock 锁定/解锁视图(lock_or_unlock_view,server: aitable)。

View Source
var ViewPresetApply = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+view-preset-apply",
	Product:     serverMain,
	Description: "按视图精确名称幂等创建或更新预设;Gantt 可用独立 timebar 完成专用两步写入",
	Intent:      "当你要部署 Grid/Kanban/Gantt/Calendar/Gallery 预设时使用;同名唯一则更新,无同名则创建,Gantt 传 --timebar 时另行写入并回读时间条。",
	Risk:        shortcut.RiskWrite,
	Safety: contract.SafetySpec{
		Effect: "write", Risk: "medium", Confirmation: "user_required", Idempotency: "idempotent",
	},
	Contract: aitableCompositeContract(
		"+view-preset-apply",
		"按视图精确名称幂等创建或更新预设;Gantt 可用独立 timebar 完成专用两步写入",
		"当你要部署 Grid/Kanban/Gantt/Calendar/Gallery 预设时使用;同名唯一则更新,无同名则创建,Gantt 传 --timebar 时另行写入并回读时间条。",
		"只做一次性新建可用 view create;同名视图不唯一或现有视图类型不同必须人工处理",
		`dws aitable +view-preset-apply --base-id B --table-id T --name "待处理" --view-type Grid --config '{"visibleFieldIds":["fld1"]}'`,
	),
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "name", Type: shortcut.FlagString, Desc: "预设视图精确名称", Required: true},
		{Name: "view-type", Type: shortcut.FlagString, Desc: "视图类型", Required: true, Enum: []string{"Grid", "Kanban", "Gantt", "Calendar", "Gallery"}},
		{Name: "config", Type: shortcut.FlagString, Desc: "目标 config JSON 对象", Required: true},
		{Name: "timebar", Type: shortcut.FlagString, Desc: "Gantt 专用 ganttTimebar JSON 对象;与通用 config 分两步写入"},
	},
	Tips: []string{`dws aitable +view-preset-apply --base-id B --table-id T --name "待处理" --view-type Grid --config '{"visibleFieldIds":["fld1"]}'`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return executeViewPresetApply(rt)
	},
}
View Source
var ViewSetFillColorRule = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+view-set-fill-color-rule",
	Product:     serverMain,
	Description: "全量覆盖 Grid 视图的条件填色规则(传 '[]' 清空)",
	Intent:      "当你要为 Grid 视图设置条件填色(按规则给符合条件的行/单元格上色),或传空数组清空所有填色规则时使用;会全量覆盖该视图的填色规则。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "view-id", Type: shortcut.FlagString, Desc: "View ID", Required: true},
		{Name: "json", Type: shortcut.FlagString, Desc: "conditionalFormats JSON 数组", Required: true},
	},
	Tips: []string{`dws aitable +view-set-fill-color-rule --base-id B --table-id T --view-id V --json '[]'`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		arr, err := parseJSONAny("json", rt.Str("json"))
		if err != nil {
			return err
		}
		return rt.CallMCP("set_view_fill_color_rule", map[string]any{
			"baseId":             rt.Str("base-id"),
			"tableId":            rt.Str("table-id"),
			"viewId":             rt.Str("view-id"),
			"conditionalFormats": arr,
		})
	},
}

ViewSetFillColorRule 更新视图数据高亮规则(set_view_fill_color_rule,server: aitable)。

View Source
var ViewSetFrozenCols = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+view-set-frozen-cols",
	Product:     serverHelper,
	Description: "设置视图冻结列数(0 表示取消冻结)",
	Intent:      "当你要冻结视图左侧若干列、以便横向滚动时保持这些列可见(0 表示取消冻结)时使用;会实际修改视图冻结列数。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "view-id", Type: shortcut.FlagString, Desc: "View ID", Required: true},
		{Name: "count", Type: shortcut.FlagInt, Desc: "冻结列数,须 >= 0", Required: true},
	},
	Tips: []string{`dws aitable +view-set-frozen-cols --base-id B --table-id T --view-id V --count 1`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("set_frozen_columns_of_view", map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
			"viewId":  rt.Str("view-id"),
			"count":   rt.Int("count"),
		})
	},
}

ViewSetFrozenCols 设置视图冻结列数(set_frozen_columns_of_view,server: aitable)。

View Source
var ViewSetRowHeight = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+view-set-row-height",
	Product:     serverHelper,
	Description: "设置视图单元格行高(像素,合法档位 32/56/88/128)",
	Intent:      "当你要调整视图行高、让内容显示更宽松或更紧凑(合法档位 32/56/88/128)时使用;会实际修改视图行高。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "view-id", Type: shortcut.FlagString, Desc: "View ID", Required: true},
		{Name: "cell-height", Type: shortcut.FlagInt, Desc: "单元格高度(像素),须 > 0", Required: true},
	},
	Tips: []string{`dws aitable +view-set-row-height --base-id B --table-id T --view-id V --cell-height 56`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("set_cell_height_of_view", map[string]any{
			"baseId":     rt.Str("base-id"),
			"tableId":    rt.Str("table-id"),
			"viewId":     rt.Str("view-id"),
			"cellHeight": rt.Int("cell-height"),
		})
	},
}

ViewSetRowHeight 设置视图行高(set_cell_height_of_view,server: aitable)。

View Source
var ViewUpdate = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+view-update",
	Product:     serverMain,
	Description: "更新视图名称 / 描述 / 配置(含类型校验后的筛选)",
	Intent:      "当你要调整视图的展示——改可见列、排序、分组或改名时使用;config.filter 复用原子入口的字段类型校验、日期/人员协议归一化并读回核对。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "table-id", Type: shortcut.FlagString, Desc: "Table ID", Required: true},
		{Name: "view-id", Type: shortcut.FlagString, Desc: "View ID", Required: true},
		{Name: "name", Type: shortcut.FlagString, Desc: "新视图名(可选)"},
		{Name: "desc", Type: shortcut.FlagString, Desc: "视图描述 JSON(可选)"},
		{Name: "config", Type: shortcut.FlagString, Desc: "视图配置更新项 JSON(可选)"},
	},
	Tips: []string{`dws aitable +view-update --base-id B --table-id T --view-id V --config '{"visibleFieldIds":["fld1"]}'`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		params := map[string]any{
			"baseId":  rt.Str("base-id"),
			"tableId": rt.Str("table-id"),
			"viewId":  rt.Str("view-id"),
		}
		if rt.Changed("name") {
			params["newViewName"] = rt.Str("name")
		}
		if rt.Changed("desc") {
			d, err := parseJSONObject("desc", rt.Str("desc"))
			if err != nil {
				return err
			}
			params["viewDescription"] = d
		}
		if rt.Changed("config") {
			c, err := parseJSONObject("config", rt.Str("config"))
			if err != nil {
				return err
			}
			if _, hasFilter := c["filter"]; hasFilter {
				return executeFilteredViewUpdate(rt, params, c)
			}
			params["config"] = c
		}
		return helpers.AnnotateViewUpdateError(rt.CallMCP("update_view", params))
	},
}

ViewCreate 创建视图(create_view)。 ViewUpdate 更新视图(update_view)。

View Source
var WorkflowDeploy = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+workflow-deploy",
	Product:     serverMain,
	Description: "创建或更新完整 workflow-dsl/v1,检查 valid/flowId;新建默认验证 STOP,--enable 验证 RUNNING",
	Intent:      "当你要一次发布并验证自动化工作流时使用;workflow-id 为空则创建并安全交付 STOP,提供则更新但不改变已有状态,--enable 会检查最终 RUNNING。",
	Risk:        shortcut.RiskWrite,
	Safety: contract.SafetySpec{
		Effect: "write", Risk: "medium", Confirmation: "user_required", Idempotency: "unknown",
	},
	Contract: aitableCompositeContract(
		"+workflow-deploy",
		"创建或更新完整 workflow-dsl/v1,检查 valid/flowId;新建默认验证 STOP,--enable 验证 RUNNING",
		"当你要一次发布并验证自动化工作流时使用;workflow-id 为空则创建并安全交付 STOP,提供则更新但不改变已有状态,--enable 会检查最终 RUNNING。",
		"只查看定义用 workflow get;只启停已有流程用 enable/disable;创建回包不确定时不要盲目重试",
		`dws aitable +workflow-deploy --base-id B --dsl '{"version":"workflow-dsl/v1","name":"提醒","nodes":[]}' --enable`,
	),
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "workflow-id", Type: shortcut.FlagString, Desc: "已有 Workflow ID;为空表示创建"},
		{Name: "dsl", Type: shortcut.FlagString, Desc: "完整 workflow-dsl/v1 JSON 对象", Required: true},
		{Name: "locale", Type: shortcut.FlagString, Default: "zh-CN", Desc: "发布 locale"},
		{Name: "enable", Type: shortcut.FlagBool, Desc: "发布后启用并从 workflow list 验证 RUNNING"},
	},
	Tips: []string{`dws aitable +workflow-deploy --base-id B --dsl '{"version":"workflow-dsl/v1","name":"提醒","nodes":[]}' --enable`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return executeWorkflowDeploy(rt)
	},
}
View Source
var WorkflowDisable = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+workflow-disable",
	Product:     serverHelper,
	Description: "禁用指定 Base 中的自动化工作流(影响业务自动化)",
	Intent:      "当你要停用某个自动化工作流时使用;会实际停用该 workflow,可能中断依赖它的业务自动化,请谨慎确认。",
	Risk:        shortcut.RiskHighWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "workflow-id", Type: shortcut.FlagString, Desc: "Workflow ID", Required: true},
	},
	Tips: []string{`dws aitable +workflow-disable --base-id B --workflow-id W`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("disable_workflow", map[string]any{
			"baseId":     rt.Str("base-id"),
			"workflowId": rt.Str("workflow-id"),
		})
	},
}

WorkflowDisable 禁用工作流(disable_workflow)。

View Source
var WorkflowEnable = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+workflow-enable",
	Product:     serverHelper,
	Description: "启用指定 Base 中的自动化工作流",
	Intent:      "当你要启用某个已配置好的自动化工作流、让它重新生效时使用;会实际把该 workflow 置为启用。",
	Risk:        shortcut.RiskWrite,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "workflow-id", Type: shortcut.FlagString, Desc: "Workflow ID", Required: true},
	},
	Tips: []string{`dws aitable +workflow-enable --base-id B --workflow-id W`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("enable_workflow", map[string]any{
			"baseId":     rt.Str("base-id"),
			"workflowId": rt.Str("workflow-id"),
		})
	},
}

WorkflowEnable 启用工作流(enable_workflow)。

View Source
var WorkflowGet = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+workflow-get",
	Product:     serverHelper,
	Description: "获取单个自动化工作流的详细信息",
	Intent:      "当你要查看某个自动化工作流的触发条件与执行动作等详情时使用;返回单个 workflow 的完整信息。",
	Risk:        shortcut.RiskRead,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "workflow-id", Type: shortcut.FlagString, Desc: "Workflow ID", Required: true},
	},
	Tips: []string{`dws aitable +workflow-get --base-id B --workflow-id W`},
	Execute: func(rt *shortcut.RuntimeContext) error {
		return rt.CallMCP("get_workflow", map[string]any{
			"baseId":     rt.Str("base-id"),
			"workflowId": rt.Str("workflow-id"),
		})
	},
}

WorkflowGet 获取工作流详情(get_workflow)。

View Source
var WorkflowList = shortcut.Shortcut{
	Service:     "aitable",
	Command:     "+workflow-list",
	Product:     serverHelper,
	Description: "列出指定 Base 中的自动化工作流(分页)",
	Intent:      "当你想了解某 Base 里配置了哪些自动化工作流及其开关状态时使用;分页返回 workflow 列表。",
	Risk:        shortcut.RiskRead,
	Flags: []shortcut.Flag{
		{Name: "base-id", Type: shortcut.FlagString, Desc: "Base ID", Required: true},
		{Name: "limit", Type: shortcut.FlagInt, Desc: "每页数量,默认 20,最大 100(可选)"},
		{Name: "all", Type: shortcut.FlagBool, Desc: "有界遍历全部工作流"},
		{Name: "page-limit", Type: shortcut.FlagInt, Default: "50", Desc: "全量遍历页数上限,1-1000"},
		{Name: "status", Type: shortcut.FlagString, Desc: "在完整集合中过滤状态,必须 --all", Enum: []string{"enabled", "disabled"}},
		{Name: "offset", Type: shortcut.FlagInt, Desc: "分页偏移量,默认 0(可选)"},
	},
	Tips:    []string{`dws aitable +workflow-list --base-id B`},
	Execute: executeWorkflowList,
}

WorkflowList 列出工作流(list_workflows)。

Functions

This section is empty.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL