Fused
BlogRSS
← All posts
Share

Letting an agent act as you, and only as you

An agent that acts for you shouldn't get your Jira login. In Fused you approve the exact actions and the account on a consent screen, and you can take the permission back whenever you like.

Say it's Friday afternoon and you ask your assistant to tidy the sprint board: close the Jira issues that shipped, and post a summary in Slack. For it to do that as you, something has to tell Jira that this agent may act on your behalf.

In most setups that something is a personal API token. You create one in Jira, paste it into the agent's config, and from then on the agent can do anything you can do in Jira, from wherever that config ends up, until somebody remembers to delete the token.

Fused takes a different route. The agent connects to a Fused MCP server, and you approve two things on a consent screen: which actions it may take, and which of your accounts it acts as. Your Jira credential stays in a Fused bucket, the agent never sees it, and taking the permission back is one click.

Here's the whole setup, start to finish.

Connect your own account first

The agent is going to act as you, so Fused needs a connection to your Jira account. You pick a reference that stands for you, and sign in once:

fused-cli service connect jira \
  --bucket personal \
  --user-ref sam \
  --scope read:jira-work \
  --scope write:jira-work

The reference sam is yours to choose, and it's how Fused will find this connection later. What happens to the connection after that (where it's stored, how it's refreshed, what happens if you revoke it in Jira) is covered in the buckets post and the connect guide.

Give the agent a server with only what it needs

An agent reaches Fused through an MCP server. Create one that exposes only the operations this job involves:

fused-cli init board-assistant --mcp \
  --description 'Tidy the sprint board and post a summary.' \
  --service 'jira=issueUpdate;slack=chatPostMessage'

The server has two operations in total. It can't delete issues, change permissions or read your DMs, because none of those are on it. If you leave out the =... part, every operation the service offers is included, which is rarely what you want for an agent. See Deploy an MCP server.

This doesn't give any agent access yet. It only sets the most an agent could ever be offered.

Connect the agent with OAuth

Add the server's Streamable HTTP URL to your MCP client. It looks like https://<engine-host>/mcp/<mcp-id>, and fused-cli app versions list board-assistant prints it. If the client gives you a choice, pick OAuth with automatic client registration.

There's no token to create, copy or store. The client sends you to Fused's sign-in page, and once you're signed in you get a consent screen. It lists the server's actions grouped by service, and nothing is ticked. You tick issueUpdate under Jira and chatPostMessage under Slack. Jira uses a connected account, so the screen also asks which of your connected Jira accounts the agent should use. You pick sam, approve, and the client is connected.

Those choices are the limits, and Fused enforces them on every call. Anything you didn't tick is refused before a request is made to Jira or Slack. The account is fixed for that connection, so a client can't ask for someone else's by sending a header. And an action added to the server later doesn't join an existing connection on its own: you reconnect and approve it.

To approve a connection you need app.mcp.use and app.mcp.tokens.manage on the server, and the Jira account has to be connected already. The stable URL follows whichever version of the server you've promoted. A URL pinned to one version works too, but a connection made at one URL can't be used at another.

Taking it back

Open Connected apps in Fused and disconnect the agent. You can also revoke the OAuth client, or revoke the execution token that came with the connection. Each one stops that connection running anything from then on, and revoking the access token also stops it refreshing its way back in.

Nothing else changes. Your Jira connection, the server and every other connection keep working, so there's no shared key to rotate and no other agent to reconfigure.

When nobody is there to click approve

OAuth needs a person at a consent screen. For an agent you run yourself, a script, or a client that doesn't do OAuth, you issue an execution token from the CLI instead. It sets the same limits, as flags:

fused-cli app token generate board-assistant friday-tidy \
  --allow issueUpdate \
  --allow chatPostMessage \
  --fixed-binding 'jira,OAuth2,sam' \
  --expires-in 4h

Each flag answers one question.

--allow lists the exact operations this token may call. Anything else is refused by Fused before a request is made to Jira or Slack. The list also shapes what the agent can see: when the agent searches the server for what it can do, operations outside the list don't come up. If you leave --allow off, the token can call everything the server exposes.

--fixed-binding says whose account to use, in the form service,auth-name,user-ref. Fused checks when you issue the token that the connection exists, and if any binding can't be resolved, no token is created. After that, the binding wins. If a request claims to be acting for someone else, Fused ignores it and uses sam. The middle value is the auth scheme name the service declares; OAuth2 is the one used for Jira in the docs.

--expires-in sets the time limit. After four hours the token stops working, and any MCP session the agent opened with it is closed.

The token is printed once. Store it in a secret manager, not in the agent's prompt. The client needs two values, the server's URL and the token in an Authorization header:

{
  "name": "board-assistant",
  "transport": "http",
  "url": "https://<engine-host>/mcp/<mcp-id>",
  "headers": {
    "Authorization": "Bearer <the friday-tidy token>"
  }
}

The agent now sees two tools, search_docs and execute. When it updates an issue, Fused attaches your Jira credential on the way out and returns Jira's response. The agent never sees the Jira token, and the Fused token in its config is useless to anyone who tries it against Jira directly. Full steps are in Connect an MCP client.

To take a token back, run fused-cli app token revoke board-assistant friday-tidy. It takes effect on every Engine in your deployment, and any MCP session still open with that token is closed. fused-cli app token list board-assistant shows what you've issued and when each one expires. Creating tokens needs its own permission, app.tokens.manage, separate from managing the server, so you can decide who builds agents' servers and who hands out access to them.

When the agent works for more than one person

With OAuth there's nothing to set up per person. Everyone who connects the assistant signs in as themselves and picks their own account on the consent screen.

A support bot that you host and that acts for whoever is asking is different, and that's where tokens still fit. Issue the token without --fixed-binding. The MCP client then names the person on each connection with an X-Fused-End-User-Ref header, and Fused uses that person's connection. The allow list and expiry still apply.

This also covers people who haven't connected yet. If an operation needs an account that isn't connected, and the MCP client supports URL elicitation, Fused sends back a sign-in link for the client to show. The person signs in, Fused stores the connection, and the client is told it's done. Fused doesn't rerun the agent's request after sign-in. Instead it tells the client whether the blocked request is safe to retry, or whether earlier steps may already have run and only the remaining work should be sent. Handle provider OAuth explains what a client has to implement.

A fixed token can't start a first-time sign-in, because its connection must already exist when you issue it. Use a dynamic token for that.

Hand over an outcome instead of operations

For work with a set order, like "close shipped issues, then post the summary", you can go one step further and give the agent a single Unified App rather than the operations underneath it. Once the app is attached to the server, the allow list takes its name:

fused-cli app token generate board-assistant friday-tidy \
  --allow unified_app:tidy_board \
  --fixed-binding 'jira,OAuth2,sam' \
  --expires-in 4h

The agent can now run tidy_board and nothing else. Your code decides the steps; the agent decides when to run them.

When the agent is told no

Sooner or later the agent asks for something it can't have, and it helps to know what that looks like.

An operation you didn't approve, or one outside a token's allow list, fails inside Fused before anything reaches Jira or Slack. If the bucket is missing a credential the operation needs, the call also stops before the provider and the error names the service and bucket to fix. If your Jira grant has lapsed for good, the connection is marked reconnect_required, and a client with URL elicitation can show you a link to sign in again.

None of these are reasons for the agent to try harder. An unexpected 401 or 403 from a provider isn't a cue to retry a write either, because the first attempt may have changed something. It's worth saying in the agent's instructions: when Fused refuses, report what was refused and stop.

Before you hand one over

The actions you approve limit which operations the agent can call, not what those operations can do inside the provider. issueUpdate can still change any field your Jira account is allowed to change. Pick actions with that in mind, and for a token keep its lifetime as short as the job.

For tokens, allow lists and bindings are set from the CLI or the API. A token created in the dashboard can call every operation on its server, with an optional expiry.

The reference pages are Create a token for an agent and Tokens for OAuth services.

Share
Save
IN THE MARGINS

Join the conversation.

A good story is the beginning of a conversation. Leave a thoughtful note.