MCP Toolbox 中的 looker-get-project-files 工具:基于 Looker SDK 枚举 LookML 项目文件
MCP Toolbox 中的 looker-get-project-files 工具:基于 Looker SDK 枚举 LookML 项目文件
导读
looker-get-project-files 是 MCP Toolbox(MCP Toolbox for Databases)为 Looker 集成提供的一个只读工具,它接收一个 project_id 参数,返回该 LookML 项目中全部文件(如 .model.lkml、.view.lkml、.explore.lkml 等)的元数据列表,是 Agent 感知 LookML 项目结构、进而深入读取与修改 LookML 的起点。读完本文,你将掌握该工具的完整配置方式、与 looker-get-projects、looker-get-project-file 的组合用法,以及它在仓库源码中的调用链路与返回字段的真实构成。
工具是什么
根据官方文档 looker-get-project-files.md 的定义:
A
looker-get-project-filestool returns all the LookML files in a project in the source.
该工具不接受其他过滤条件,唯一必需的入参是 project_id,用于定位 LookML 项目。它的典型输出是“一个 JSON 数组,数组元素是代表单个 LookML 文件的对象,包含 path、id、type、git_status 等详情”。
从语义上讲,它是一条**枚举型(List)**查询:只做读取、不做任何写操作,因此在源码实现中被标注为只读工具(详见下文“源码级实现”)。
在 LookML 工具链中的位置
looker-get-project-files 不是孤立存在的,它处在 Looker 集成工具链的中间环节,与相邻两个工具配合构成“先找项目 → 再列文件 → 再读内容”的典型流程:
| 工具 | 入参 | 作用 | 文档 |
|---|---|---|---|
looker-get-projects |
无 | 返回源上所有 LookML 项目,输出 project_id 与 project_name |
looker-get-projects.md |
looker-get-project-files |
project_id(必填) |
返回指定项目内的所有 LookML 文件列表 | looker-get-project-files.md |
looker-get-project-file |
project_id、file_path(均必填) |
返回指定文件的原始文本内容 | looker-get-project-file.md |
三者协作时,project_id 由 get_projects 获得,file_path 通常由 get_project_files 的输出提供。预构建配置 looker-dev.yaml 中同时装配了这三个工具(其中 get_project_files 定义在第 117–129 行),说明它们被设计为配套使用的开发工具集。
在 Toolbox 配置文件中启用
在 MCP Toolbox 的配置文件中,工具以 kind: tool 的 YAML 文档形式声明。官方文档给出的完整示例如下:
kind: tool
name: get_project_files
type: looker-get-project-files
source: looker-source
description: |
This tool retrieves a list of all LookML files within a specified project,
providing details about each file.
Parameters:
- project_id (required): The unique ID of the LookML project, obtained from `get_projects`.
Output:
A JSON array of objects, each representing a LookML file and containing
details such as `path`, `id`, `type`, and `git_status`.
字段参考
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
type |
string | 是 | 必须为 "looker-get-project-files",工具注册标识,不可改动 |
source |
string | 是 | 指向 Looker 源实例的名称,须与 kind: source 中定义的源名一致 |
description |
string | 是 | 工具描述,会原样传递给 LLM,作为模型理解工具用途与参数的依据 |
name |
string | 是 | 工具在 MCP 命名空间中的调用名(如 get_project_files),来自所有工具共用的 ConfigBase |
annotations |
对象 | 否 | 可选的 MCP Tool Annotations,如 readOnlyHint、idempotentHint 等 |
其中 name 与可选的 annotations 字段来自通用工具框架 tools.go 中定义的 ConfigBase,对所有类型工具统一生效。
必填与严格校验
从测试用例可以确认,配置解析是严格的:在 lookergetprojectfiles_test.go 中,TestFailParseFromYamlLookerGetProjectFiles 验证了在 looker-get-project-files 配置里加入不存在的 method: GOT 字段会直接报错 unknown field "method"。因此配置时只需、也只能使用上表列出的字段。同时,Initialize 中规定 description 为空时会返回错误 description is required for tool(见 lookergetprojectfiles.go),这与文档中 description 标为必填一致。
前置条件:Looker Source 的配置
该工具依赖 source 字段指向的 Looker 源。Source 的配置方式见 Looker 源文档,核心要点如下:
kind: source
name: looker-source
type: looker
base_url: ${LOOKER_BASE_URL}
client_id: ${LOOKER_CLIENT_ID:}
client_secret: ${LOOKER_CLIENT_SECRET:}
verify_ssl: ${LOOKER_VERIFY_SSL:true}
timeout: 600s
use_client_oauth: ${LOOKER_USE_CLIENT_OAUTH:false}
show_hidden_models: ${LOOKER_SHOW_HIDDEN_MODELS:true}
show_hidden_explores: ${LOOKER_SHOW_HIDDEN_EXPLORES:true}
show_hidden_fields: ${LOOKER_SHOW_HIDDEN_FIELDS:true}
base_url:Looker 服务器地址,不要带尾部斜杠;本地部署时可能需要追加 API 端口,如https://looker.example.com:19999。verify_ssl:除非使用自签名证书,否则应保持true;任何非true的值都会被解释为false。client_id/client_secret:Looker 服务器分配的随机字符序列;若使用 Looker OAuth 或客户端授权转发则无需填写。use_client_oauth:设为'true'时转发客户端 OAuth 访问令牌;为空字符串或'false'时禁用(默认禁用)。该配置与下文“客户端授权转发”直接相关。- 敏感信息建议使用
${ENV_NAME}环境变量替换而非硬编码。
源码级实现:一条从 YAML 到 Looker API 的完整链路
该工具的完整实现位于 lookergetprojectfiles.go,从注册到执行共四步,每一环都能在仓库源码中找到对应证据。
1. 类型注册
resourceType 常量被定义为 "looker-get-project-files",在包的 init() 中通过 tools.Register(resourceType, newConfig) 注册到全局工具注册表(tools.go)。如果该类型已被注册,Register 返回 false 并触发 panic,避免重复注册。配置解析时,框架按 type 在注册表中查找到 newConfig,用 YAML 解码器填充 Config 结构体。
2. 参数声明
Initialize 阶段声明了唯一的入参:
projectIdParameter := parameters.NewStringParameter("project_id", "The id of the project containing the files")
project_id 是必填字符串参数,其描述为“包含这些文件的项目 id”。随后该参数被打包进 parameters.Parameters,并生成工具 Manifest(含描述、参数定义与 authRequired),最终在 Invoke 中通过 params.AsMap() 取出:
projectId, ok := mapParams["project_id"].(string)
if !ok {
return nil, util.NewAgentError(fmt.Sprintf("'project_id' must be a string, got %T", mapParams["project_id"]), nil)
}
如果模型传来的不是字符串类型,会返回 Agent 级错误。这是对文档“接受一个 project_id 参数”的代码级落实。
3. 调用 Looker SDK 的 AllProjectFiles
核心调用发生在 Invoke 中:
sdk, err := source.GetLookerSDK(ctx, string(accessToken))
...
resp, err := sdk.AllProjectFiles(projectId, "", source.LookerApiSettings())
GetLookerSDK由 Looker 源实现(见 looker.go):若源启用了use_client_oauth,则使用携带客户端访问令牌的传输层创建全新 SDK(并转发X-Forwarded-For、X-Real-IP等客户端 IP 头);否则复用源初始化时基于client_id/client_secret建立的共享客户端。AllProjectFiles(projectId, "", apiSettings)是 Looker SDK(looker-open-source/sdk-codegen/go的 v4 版本)的 API 调用,第二个参数传空字符串表示不限制返回字段(fields)。
4. 响应整理与返回
AllProjectFiles 返回的是 Looker SDK 的 ProjectFile 对象列表,工具将其逐个转换为 map 并只保留非空字段:
vMap := make(map[string]any)
if v.Id != nil { vMap["id"] = *v.Id }
if v.Path != nil { vMap["path"] = *v.Path }
if v.Title != nil { vMap["title"] = *v.Title }
if v.Type != nil { vMap["type"] = *v.Type }
if v.Extension != nil { vMap["extension"] = *v.Extension }
if v.Editable != nil { vMap["editable"] = *v.Editable }
data = append(data, vMap)
因此,实际返回的每个文件对象可能包含 id、path、title、type、extension、editable 等键,空值键会被省略。文档示例描述中提到的 git_status 属于对 Looker SDK ProjectFile 完整模型的描述;从当前源码看,工具实际输出的键集为上述六个。这一点在依赖输出时值得留意——例如把 path 传给 looker-get-project-file 读取文件内容时,应以实际返回的非空键为准。
错误处理
- 若 Looker API 返回
401(在错误信息中匹配status=401),工具将其转换为http.StatusUnauthorized的客户端-服务器错误; - 其他错误统一交给
util.ProcessGeneralError处理; - 若
source类型与工具不兼容(未实现compatibleSource接口),会返回“invalid source for tool”错误。
只读语义与客户端授权转发
该工具在 Initialize 中使用了默认的只读注解:
tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations)
NewReadOnlyAnnotations 会设置 ReadOnlyHint: true(见 tools.go),向 MCP 客户端声明这是一个无副作用的只读工具。
此外,该工具实现了 RequiresClientAuthorization 与 GetAuthTokenHeaderName 两个方法:当 Looker 源开启 use_client_oauth 时,工具会要求客户端授权,并把访问令牌连同 Authorization(或自定义的认证头)转发给 Looker SDK。这意味着在企业场景中,可以做到按最终用户的 OAuth 令牌去列举其有权限访问的项目文件,而不是统一使用服务账号凭据。
测试如何验证该工具
仓库为该工具提供了单元测试 lookergetprojectfiles_test.go:
TestParseFromYamlLookerGetProjectFiles:验证标准的 YAML 片段(type: looker-get-project-files、source: my-instance、description)能被正确解析为lkr.Config,且authRequired默认为空切片;TestFailParseFromYamlLookerGetProjectFiles:验证未知字段(如method)会导致解析失败。
这两类测试说明:工具的配置面(YAML → Config)是严格受控的,任何超出文档字段表的键都会被拒绝,从侧面保障了“配置即契约”的可靠性。
典型使用流程小结
在 MCP Toolbox 中启用 Looker 开发工具集(可直接使用预构建配置 looker-dev.yaml,其中已包含 get_project_files 定义)后,一个完整的工作流是:
- 调用
get_projects获取源上的所有 LookML 项目,记录目标project_id; - 调用
get_project_files,传入project_id,得到项目内全部 LookML 文件的列表(含path、type、extension等); - 对感兴趣的文件调用
get_project_file,传入project_id与file_path,读取其原始内容; - 如需修改,再进入
create_project_file、update_project_file等开发模式工具。
在整个链路中,looker-get-project-files 承担着“项目目录索引”的角色——它本身不返回文件内容,却是模型了解项目构成、决定下一步读写动作的关键输入。配置时只需牢记三点:type 固定为 looker-get-project-files、source 指向已配置好的 Looker 源、description 写清参数与输出以引导 LLM 正确使用。