
Every school morning, my family’s schedule is technically available. It’s also buried across enough calendars and apps that nobody wants to hunt for it before breakfast.
With four kids, our calendar looks like an airport departure board: soccer, theater, lacrosse, ballet, school events and, occasionally, dinner.
I wanted each kid to get a quick summary without building another app they’d ignore. I also wanted an excuse to buy a thermal receipt printer.
So I built The DailyCrew Wire, a personalized newspaper printed on 80 mm receipt paper. Each edition includes the weather, the kid’s schedule, dinner plans, a helpful task and something fun, such as a fact, riddle or joke.
The printer was the fun part. Connecting DailyCrew to Google Calendar, OpenWeather and API Ninjas was the work. Each service brought its own authentication, keys, token storage, refresh logic and conventions.
Composio replaced that mess with one connection layer. DailyCrew can call a specific tool for predictable jobs or let an agent choose from a small set when the content needs judgment.
Composio replaced three authentication flows
DailyCrew uses three Composio toolkits:
- Google Calendar provides each kid's schedule.
- OpenWeather provides the forecast.
- API Ninjas provides facts, trivia, riddles, history, and jokes.
DailyCrew creates a Composio session for the household operator and enables the toolkit and, when needed, the specific tools for that run.
function createComposioSession(
userId: string,
toolkit: string,
tools?: string[],
) {
return composio.sessions.create(userId, {
toolkits: [toolkit],
...(tools ? { tools: { [toolkit]: tools } } : {}),
manageConnections: false,
sandbox: { enable: false },
});
}
A session scopes the user, connected accounts, and available tools. The user is the household operator, not each child. The kids receive newspapers. They do not need accounts. Composio recommends a stable application-defined ID because that ID determines which connected accounts are available to the session (Composio session documentation).
Connecting a service follows the same pattern:
const session = createComposioSession(
userId,
"googlecalendar",
);
const connection = await session.authorize(
"googlecalendar",
);
console.log(connection.redirectUrl);
DailyCrew exposes the connections through a few CLI commands:
dailycrew connect calendar
dailycrew connect openweather
dailycrew connect api-ninjas
The Google Calendar command opens an OAuth flow. OpenWeather and API Ninjas collect their API keys through the connection page. Composio handles those differences and associates each connected account with the same household user. Later sessions can reuse the accounts.
I do not store Google tokens, build an OAuth callback, refresh expired credentials, or create encrypted storage for third-party API keys. Composio owns the credential lifecycle (manual authentication documentation). I still normalize the returned calendar and weather data into DailyCrew's types. That is application logic. Authentication stays out of the application.
Known tasks get direct calls
The distinction is simple. DailyCrew makes direct calls when it already knows the tool and arguments. It gives an agent a choice only when the choice itself adds value.
Calendar data is predictable. DailyCrew knows which calendar to read, the date range, the time zone, and the sort order. There is no useful decision for an agent to make, so it executes the Google Calendar tool directly:
const result = await session.execute(
"GOOGLECALENDAR_EVENTS_LIST",
{
calendarId,
timeMin,
timeMax,
timeZone,
singleEvents: true,
orderBy: "startTime",
showDeleted: false,
},
);
Weather uses the same direct pattern with OPENWEATHER_API_GET5_DAY_FORECAST. DailyCrew passes the location, requests English JSON in imperial units, selects the requested local day, and turns the forecast into a high, low, and short description.
In both cases, DailyCrew chooses the tool and supplies the arguments. Composio provides authenticated execution. Direct calls are useful when I want a known result without maintaining the integration myself.
Editorial tasks benefit from agent choice
The "Just for Fun" section needs a small editorial decision. API Ninjas has six tools that could produce something interesting for the receipt:
const FUN_TOOLS = [
"API_NINJAS_GET_FACT_OF_THE_DAY",
"API_NINJAS_GET_FACTS",
"API_NINJAS_GET_TRIVIA",
"API_NINJAS_GET_RIDDLES",
"API_NINJAS_GET_DAY_IN_HISTORY",
"API_NINJAS_GET_DAD_JOKE",
] as const;
I could choose one randomly or rotate through the list. Instead, I wanted the choice to respond loosely to the day. A rainy morning might inspire a weather fact. A calendar event might make a piece of trivia or a riddle fit. The same kid should not get the same kind of feature every morning.
For this path, DailyCrew creates a Composio client with the Vercel AI SDK provider:
const composio = new Composio({
provider: new VercelProvider({ strict: true }),
});
The session exposes the API Ninjas toolkit and only the six read-only tools in FUN_TOOLS:
const session = await composio.sessions.create(
userId,
{
sessionPreset: "direct_tools",
toolkits: ["api_ninjas"],
tools: {
api_ninjas: [...FUN_TOOLS],
},
manageConnections: false,
sandbox: { enable: false },
},
);
session.tools() returns those tools in the format expected by the Vercel AI SDK:
const tools = await session.tools();
const research = await generateText({
model,
tools,
toolChoice: "required",
stopWhen: stepCountIs(1),
prompt: editionContext,
});
Composio's Vercel provider creates the schemas the model sees and supplies the execution function for its tool call. I did not define an AI SDK tool for every API Ninjas endpoint or write a switch statement to route each model-selected call to the correct API (Vercel AI SDK provider documentation).
The model gets the date, weather, and calendar context, then chooses one available tool. Composio executes the tool with the household user's connected API Ninjas account.
My commit history contains this commit:
Make it not use only dad jokes
The first version discovered that the dad-joke tool was an easy answer and kept choosing it. Every kid, every day: dad joke. I adjusted the prompt to encourage more variety among facts, trivia, riddles, and history. Dad jokes remain available. I am still a dad, after all.
I set the safety boundary
The agent can select from six read-only API Ninjas tools. It cannot search every Composio integration, change a calendar event, or send anything to the printer.
That boundary belongs to the application. Composio handles the tool definitions and authenticated execution. DailyCrew decides which choices are safe and useful.
This split lets one application use both execution styles without giving the agent control over every task. Calendar and weather calls stay deterministic. Only the playful editorial choice goes to the model.
I left the connection code unwritten
Without Composio, DailyCrew would need some combination of:
- A Google OAuth application
- Authorization and callback routes
- Token storage and refresh handling
- A way to collect and protect OpenWeather API keys
- Another connection flow for API Ninjas
- Handwritten tool schemas for the agent
- Execution handlers that translate every model tool call into an API request
- Error handling for each provider's authentication behavior
None of this is impossible. It is also not why I started the project. LLMs make it practical to write code that might otherwise stay unwritten, but generating code does not remove the integration and maintenance work.
Composio removed enough of that work for me to focus on the parts specific to my family: which calendars belong to each kid, how dinner gets pulled from the family schedule, and whether a receipt printer can produce a recognizable ballet icon. That icon took longer than I would like to admit.
Three APIs became one small newspaper
Running DailyCrew now looks like this:
dailycrew print
The application fetches the household weather once. For each child, it directly retrieves the correct Google Calendar events through Composio. It then gives the agent the limited set of API Ninjas tools and asks for one playful item. The remaining work is local and predictable: lay out the newspaper and send it to the printer.
A printed edition might contain scattered clouds, soccer at 4:00, chicken tacos for dinner, and a riddle about a scarecrow. The kids never need to know that three external services were involved.
Honestly, I would prefer that they did not. Explaining OAuth before breakfast sounds worse than managing four calendars.