5.8 Search
Parent: Story 5 (#7232)
Tool file: new forge/ee/lib/mcp/tools/search.js
Summary
Global search across a team's resources, and an instance-scoped search. Read-only, useful for agent situational awareness ("find the instance named X"). This scope is entirely read-only: there are no write or destructive endpoints.
Read tools (phase 1)
readOnlyHint: true.
| Tool |
Endpoint |
Scope |
Annotation |
platform_search_team_resources |
GET /search?team=...&query=... |
(manual team:search) |
read |
platform_search_instances |
GET /search/instances?team=...&query=... |
(manual team:search) |
read |
Design notes:
- Neither route uses
needsPermission. Gating is manual inside the handler: request.session.User.admin || app.hasPermission(membership, 'team:search'). GET /search returns an empty result set (not 403) when the caller lacks team:search; GET /search/instances returns 403.
GET /search further filters results per-application via project:read (admin bypass; devices with no application always shown), so a team-scoped PAT sees a filtered subset. GET /search/instances does not apply per-row project:read filtering (it returns the full team result set after the team:search gate).
- Both require a
team query param. The tool inputSchema should require teamId and query.
team:search is a team-role scope (not admin). Add it to the allow-list and access-tag it read.
Scopes to allow-list + access-tag (#7445): team:search (read).
Tool definitions (description + zod inputSchema):
platform_search_team_resources: {
description: 'Search across the resources of one team (global team search). An empty or blank query returns an empty result set.',
inputSchema: z.object({
teamId: z.string().describe('Team id (opaque hashid), carried as the `team` query param'),
query: z.string().describe('Search term')
})
}
platform_search_instances: {
description: 'Search the instances of one team. An empty or blank query returns an empty result set.',
inputSchema: z.object({
teamId: z.string().describe('Team id (opaque hashid), carried as the `team` query param'),
query: z.string().describe('Search term')
})
}
Tests:
- Both tools allowed for read-only PAT.
- Caller without
team:search gets empty results (global) / 403 (instances) as per the route behaviour.
- Team-scoped PAT restricted to in-scope team.
5.8 Search
Parent: Story 5 (#7232)
Tool file: new
forge/ee/lib/mcp/tools/search.jsSummary
Global search across a team's resources, and an instance-scoped search. Read-only, useful for agent situational awareness ("find the instance named X"). This scope is entirely read-only: there are no write or destructive endpoints.
Read tools (phase 1)
readOnlyHint: true.platform_search_team_resourcesGET /search?team=...&query=...team:search)platform_search_instancesGET /search/instances?team=...&query=...team:search)Design notes:
needsPermission. Gating is manual inside the handler:request.session.User.admin || app.hasPermission(membership, 'team:search').GET /searchreturns an empty result set (not 403) when the caller lacksteam:search;GET /search/instancesreturns 403.GET /searchfurther filters results per-application viaproject:read(admin bypass; devices with no application always shown), so a team-scoped PAT sees a filtered subset.GET /search/instancesdoes not apply per-rowproject:readfiltering (it returns the full team result set after theteam:searchgate).teamquery param. The tool inputSchema should requireteamIdandquery.team:searchis a team-role scope (not admin). Add it to the allow-list and access-tag itread.Scopes to allow-list + access-tag (#7445):
team:search(read).Tool definitions (description + zod inputSchema):
Tests:
team:searchgets empty results (global) / 403 (instances) as per the route behaviour.