Expose a STACK proxy call as a LangChain tool. Each job starts a Mission with the access it needs, and tool calls use its Passport. The connected service credential stays in STACK.
pip install langchain langchain-openai getstackFollow Deploying agents to connect Slack, approve the fleet policy and enroll the worker. Set STACK_AGENT_ID to that worker’s ID and keep its enrolled key available to the runtime. The operator’s credential stays in the setup process.
Save the shared tool as fleet_support_tool.py and the Mission request as mission.json. Replace the policy and connection IDs with your own, and use the channel allowed by your policy. Keep the job’s idempotency key when retrying that job.
Configure the framework’s model provider separately. This example routes the Slack tool through STACK. Its model calls use the framework’s own connection.
import hashlib
def start_support_job(stack, request, provider_url="https://slack.com/api/chat.postMessage"):
started = stack.missions.start(request)
operation_id = hashlib.sha256(
(started["mission_id"] + ":support-update").encode()
).hexdigest()
sent = False
def post_support_update(text: str) -> dict:
"""Post this job's support update to Slack channel C123."""
nonlocal sent
response = stack.proxy.request(
"slack", provider_url, "POST",
body={"channel": "C123", "text": text},
passport_token=started["passport"]["token"],
authority_request_id=operation_id,
)
if response.status != 200 or not response.body.get("ok", False):
raise RuntimeError("Slack did not accept the update")
sent = True
return response.body
def finish():
if not sent:
raise RuntimeError("The support update has not been sent")
stack.passports.checkout(
started["passport"]["jti"], services_used=["slack"],
actions_count=1, summary="Posted the support update",
)
return stack.missions.complete(started["mission_id"], output={"done": True})
def stop():
return stack.missions.revoke(started["mission_id"], reason="operator_initiated_clean")
return started, post_support_update, finish, stop
{
"fleet_policy_id": "flp_your_policy",
"idempotency_key": "support-ticket-4821-update",
"intent_summary": "Post the support update for ticket 4821",
"requested_access": [
{
"connection_id": "scon_your_slack_account",
"scopes": [
"chat:write"
],
"intents": [
{
"name": "slack.post_to_channel",
"params": {
"channel": "C123"
}
}
],
"constraints": [],
"allow_data_from": []
}
],
"requested_operations": [],
"limits": {
"max_actions": 20,
"max_concurrent_actions": 1,
"max_duration_seconds": 900
},
"expected_output_shape": {
"type": "object",
"required": [
"done"
],
"properties": {
"done": {
"const": true
}
},
"additionalProperties": false
}
}import json
import os
from getstack import Stack
from fleet_support_tool import start_support_job
stack = Stack(agent_id=os.environ["STACK_AGENT_ID"], base_url=os.environ.get("STACK_API_URL", "https://api.getstack.run"))
with open("mission.json", encoding="utf-8") as source:
request = json.load(source)
started, post_support_update, finish, stop = start_support_job(stack, request)
from langchain_core.tools import tool
from langchain.agents import create_agent
agent = create_agent(
model="openai:gpt-4o-mini",
tools=[tool(post_support_update)],
system_prompt="Post the supplied support update once using the tool.",
)
try:
agent.invoke({"messages": [{"role": "user", "content": "Ticket 4821 has been resolved."}]})
finish()
except Exception:
stop()
raise
Starting the Mission returns its ID and Passport. The shared tool sends that Passport on the Slack request. STACK checks the endpoint, channel and live limits before adding the stored credential. A request for another channel is refused even if the tool is changed.
The example posts one update per job. Its operation ID stays the same across retries, so an unchanged retry cannot send another message. Give a genuinely different action its own operation ID.
This example uses an allow rule and stops the Mission on any error. A review rule instead returns an approval reference before dispatch. A runtime handling review must keep the Mission open while it waits, then retry the unchanged request after approval. That approval permits one action and does not expand the fleet policy.
The example checks out the Passport and completes the Mission after the tool confirms delivery. An error revokes the Mission so it cannot accept further calls.
For work that continues beyond this short example, record checkpoints and use stack.missions.renew(mission_id) to obtain the next Passport. Use the returned token for later calls. Renewal retains the same Mission, scope and usage.
Revoke a Mission to stop that job, or revoke fleet membership to stop one worker’s fleet access. STACK rejects the next call or renewal that depends on revoked access.