Downcity
Services

Service vs Tools

Understand when to write a tool, when to write a service, and how the two can work together

Service vs Tools

This is one of the easiest extension decisions to get wrong at first.

One-sentence version

  • a tool is better for one call
  • a service is better for a long-lived owner

Better fits for a tool

  • one-shot functional capability
  • one query
  • one transformation
  • no long-lived state
  • no background lifecycle

Examples:

  • reading one file
  • calling one external API
  • performing one text transformation

Better fits for a service

  • needs long-lived state
  • needs a worker
  • owns a connection or session pool
  • should be shared across many sessions
  • groups a family of related actions under one long-lived object

Examples:

  • chat channel integration
  • shell session management
  • memory runtime
  • task scheduler

Three practical judgment calls

"I only need one execution capability"

Prefer a tool first.

"I need a runtime object that stays alive"

Prefer a service.

"I need long-lived state, but I also want model-facing tool-style calls"

A common pattern is:

  • the service owns the long-lived state
  • a tool acts as the thin facade into that service or its runtime

This kind of combination is especially common for shell-like and chat-like capabilities.

Practical advice for SDK users

For your first extension, do not default everything into services.

The safer sequence is usually:

  1. confirm whether a simple tool is enough
  2. only promote it to a service when you clearly need long-lived state

A useful rule of thumb

If you find yourself adding these around a tool:

  • a global map
  • a long-lived connection
  • a worker
  • cache lifecycle logic

that usually means the capability is starting to look like a service.