feat(platformtools): thread ToolCallEnv + add Slack tools#2327
feat(platformtools): thread ToolCallEnv + add Slack tools#2327
Conversation
Extend the core PlatformToolExecutor.Call interface with a toolconfig.ToolCallEnv parameter so platform tools can read per-call OAuthToken, UserConfig, SystemEnv, and GramEmail. The signature change is atomic: all implementers (logs.SearchLogs, triggers.ListTriggers, triggers.ConfigureTrigger, new slack.* tools), the single runtime caller (platformtools/runtime.Service.ExecuteTool) and the gateway.PlatformExecutor interface consumed by the tool proxy flip together, because splitting the change breaks compilation. Add a SlackHTTPClient *guardian.HTTPClient field to the platformtools Dependencies struct and a matching WithSlackHTTPClient runtime option so the Slack tools dial through the project's DNS allowlist / connection pool. Register 8 Slack Web API tools under server/internal/platformtools/slack/: read channel / thread / user profile, search channels / messages / users, schedule message, and send message. Each tool reads its OAuth token from the threaded env and returns a normalized JSON payload. Tools are wired through the existing registry.go factory list but remain unused until the assistants runtime wiring lands in a later change. ✻ Clauded...
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
🦋 Changeset detectedLatest commit: d76d232 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
…CallEnv.Merged Slack tools were reading from the process env via os.LookupEnv, mixing the pod's config with per-call env. Remove the envLookupFunc plumbing entirely, thread toolconfig.ToolCallEnv through the token lookup, and wire guardianPolicy.PooledClient() at both platformtoolsruntime.NewService call sites so the registered Slack tools actually have an HTTP client at runtime. Adds ToolCallEnv.Merged() so the UserConfig-over-SystemEnv merge is a single named helper. Collapses the inline merge at gateway/security.go and uses it in Slack's token lookup. Also addresses Devin findings: o11y.NoLogDefer for resp.Body.Close and t.Context() in tests. ✻ Clauded...
| func(deps Dependencies) PlatformToolExecutor { | ||
| return platformslack.NewReadChannelMessagesTool(deps.SlackHTTPClient) | ||
| }, | ||
| func(deps Dependencies) PlatformToolExecutor { | ||
| return platformslack.NewReadThreadMessagesTool(deps.SlackHTTPClient) | ||
| }, | ||
| func(deps Dependencies) PlatformToolExecutor { | ||
| return platformslack.NewReadUserProfileTool(deps.SlackHTTPClient) | ||
| }, | ||
| func(deps Dependencies) PlatformToolExecutor { | ||
| return platformslack.NewSearchChannelsTool(deps.SlackHTTPClient) | ||
| }, | ||
| func(deps Dependencies) PlatformToolExecutor { | ||
| return platformslack.NewSearchMessagesAndFilesTool(deps.SlackHTTPClient) | ||
| }, | ||
| func(deps Dependencies) PlatformToolExecutor { | ||
| return platformslack.NewSearchUsersTool(deps.SlackHTTPClient) | ||
| }, | ||
| func(deps Dependencies) PlatformToolExecutor { | ||
| return platformslack.NewScheduleMessageTool(deps.SlackHTTPClient) | ||
| }, | ||
| func(deps Dependencies) PlatformToolExecutor { | ||
| return platformslack.NewSendMessageTool(deps.SlackHTTPClient) | ||
| }, | ||
| } |
There was a problem hiding this comment.
🚩 Slack tools are always registered even when no Slack token is configured
All eight Slack tools are unconditionally added to the platform tools registry at server/internal/platformtools/registry.go:27-51, regardless of whether a Slack token is configured. This means ListPlatformTools() and ListTypedTools() will advertise Slack tools to clients even when no token exists, and calling them will produce a runtime error ('slack token not configured'). This is consistent with how trigger tools behave (they're registered even when TriggerApp is nil and return errors at call time), so this appears to be a deliberate pattern — but it could lead to a poor UX where AI agents repeatedly attempt to use Slack tools that will always fail.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
I recommend splitting this up to tool_read_channel_messages{,_test}.go, tool_search_channels{,_test}.go, etc... since it makes tools apparent to agents via the filesystem.
There was a problem hiding this comment.
in line with other comment rename this file to tool_search_logs.go and ultimately we'll be able to tell agents to look for tool_*.go to quickly locate platform tools.
Address review feedback: one `tool_*.go` file per Slack tool, rename `logs/search_logs.go` to follow the same convention, and simplify the changeset for customer-facing release notes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
toolconfig.ToolCallEnvparam tocore.PlatformToolExecutor.Call. All existing implementers (logs, triggers) plus the singleruntime/service.ExecuteToolcaller flip together — atomic to keep compilation green.SlackHTTPClient *guardian.HTTPClientfield toplatformtools.Dependenciesand aWithSlackHTTPClientoption.server/internal/platformtools/slack/(read channel / thread / user profile, search channels / messages / users, schedule / send message) registered through the existingregistry.gofactory list.OAuthTokenfrom the new env param; pooledguardian.HTTPClientdials via the project's DNS / allowlist guardrails.Linear: https://linear.app/speakeasy/issue/AGE-1896/featplatformtools-thread-toolcallenv-add-slack-tools
✻ Clauded...