Connect Your Own App to Cogny Automations
Expose your app's data and actions as an MCP endpoint, register it with Cogny, and run a scheduled agent against it. The full loop a coding agent can do end to end with the Cogny CLI.
Cogny automations are scheduled agents that act through tools you have greenlit. Most of those tools come from Cogny's own integrations: Google Ads, Meta, GA4, Discord, and so on. When the data or the action lives in your own app (a booking system, a member database, an internal admin), you expose it as an MCP endpoint, register it with Cogny, and the automation calls it like any other integration.
This guide is written for a coding agent or developer working in the user's codebase. Everything below can be done from the terminal with the Cogny CLI. The dashboard covers the simpler cases (one tool per job); see the end of this page.
When to use this
- Your app holds data Cogny can't reach through a built-in integration, and a weekly or daily agent should act on it.
- The action needs rules only your app can enforce: consent, opening hours, per-customer limits, an approval queue.
- You want every run recorded as a ticket, with a transcript of what the agent read and did.
If you only need a nightly data sync with no judgement involved, use Scripts instead (Automations and content loops).
The example: a retention agent for a membership business
A martial-arts school, gym or dance studio has the same pattern: members who miss two weeks tend to quit. The agent runs once a week. It finds members who have not been seen in 14 days, books each a make-up class, and texts the family in the instructor's voice.
The app exposes six tools:
| Tool | Kind | What it does |
|---|---|---|
get_retention_report | read | At-risk members with last visit, recent attendance, contact, text consent and make-up options |
get_student | read | One member's profile, notes, history and messages already sent |
list_class_schedule | read | Upcoming classes with ids and dates |
book_makeup_class | write | Books a member into a class on a date |
send_parent_text | write | Texts the family, or queues the text for staff approval |
log_parent_message | write | Records a call, email or note |
1. Log in to the user's workspace
Ask the user for an API key: Settings → Data & Integrations → Connect AI Clients, cogny cli tab. The key is shown once. The user already has a workspace, so don't run cogny init; that creates a new one.
alias cogny="npx @cogny/cli"
cogny login --api-key <key>
cogny status # subscription, credits, connected integrations
cogny tools list # automation tools appear only if automations are on
Automations are included with Cloud. If create_automation is missing from cogny tools list, automations aren't switched on for this workspace; tell the user to contact Cogny support.
2. Build the MCP endpoint
Cogny talks to your endpoint over Streamable HTTP (JSON-RPC over POST) and sends Authorization: Bearer <api_key> on every call. A minimal Next.js route with mcp-handler and zod:
// app/api/agent/mcp/route.ts
import crypto from 'node:crypto';
import { createMcpHandler } from 'mcp-handler';
import { z } from 'zod';
const handler = createMcpHandler((server) => {
server.registerTool('get_retention_report', {
description: 'Members not seen for 14+ days, with make-up options for the next 7 days.',
inputSchema: z.object({ at_risk_only: z.boolean().default(true) }),
annotations: { readOnlyHint: true },
}, async ({ at_risk_only }) => json(await retentionReport(at_risk_only)));
server.registerTool('send_parent_text', {
description: 'Text a family. Refused without consent or outside 8am–9pm local time.',
inputSchema: z.object({ student_id: z.string().uuid(), message: z.string().max(300) }),
annotations: { readOnlyHint: false, destructiveHint: false },
}, async (input) => json(await sendText(input))); // consent, quiet hours, opt-out line enforced here
});
// Constant-time compare, so the key can't be guessed from response timing.
function validKey(req: Request) {
const key = process.env.AGENT_API_KEY;
const got = (req.headers.get('authorization') || '').replace(/^Bearer\s+/i, '');
if (!key) return false;
const a = Buffer.from(got), b = Buffer.from(key);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
async function authed(req: Request) {
if (!validKey(req)) return new Response('Unauthorized', { status: 401 });
return handler(req);
}
export { authed as GET, authed as POST };
json, retentionReport and sendText stand for your own code. Generate the key with openssl rand -hex 32, set it on the server, and deploy.
Two rules decide what a run can do:
- Read tools must be recognisable. Unless you list read tools yourself, a run is offered a tool on a granted server when its name starts with
get,list,search,execute,inspect,run,fetch,query,read,describe,find,check,count,lookup,vieworshow(a leadingtool_is ignored). A tool annotatedreadOnlyHint: truealso counts, unless it is alsodestructiveHint: true. A tool calledretention_reportwith no annotation is left out, and the agent can't see it. Annotate it, rename it, or list it inread_tools(step 4). - Safety rules go in the write tools, not the prompt. Check consent, quiet hours, rate limits and opt-out inside
send_parent_text, and return a clear error when a call is refused. The agent reads the error and moves on. A prompt can be misread; your endpoint can't be talked out of a rule. If staff should see messages before they go out, make the write tool queue a draft for approval in your admin rather than send.
3. Register the endpoint
cogny tools call add_custom_mcp_server --input '{
"name": "Dojo",
"server_url": "https://example-dojo.com/api/agent/mcp",
"api_key": "<AGENT_API_KEY>",
"description": "Members, attendance, make-up classes and family texts"
}'
The result has a server_id and every discovered tool with read_only: true|false. false means the tool wasn't recognised as read-only, not that it must be a write. A tool that really only reads but shows false can go in read_tools; a write tool goes in action_grants. Only owners and admins can register servers, and the URL must be https. Call it again with the same name to change the URL or rotate the key.
After you deploy changes to the endpoint, run cogny tools call refresh_mcp_discovery --input '{"server_name":"Dojo"}' so Cogny sees the new tool list.
In the dashboard, the same step is Settings → MCPs For Cogny → MCP Servers → Add Server, with auth type API key.
4. Create the automation, disabled
cogny tools call create_automation --input '{
"name": "Weekly retention",
"crontab": "0 10 * * 1",
"timezone": "America/Indiana/Indianapolis",
"enabled": false,
"instructions": "Call get_retention_report. For each at-risk member: read get_student, skip anyone whose notes say not to contact or who was contacted this week, book one make-up class with book_makeup_class, then send_parent_text if they have text consent (warm, under 300 characters, first name, mention the class day and time). Without consent, log_parent_message a note that a call is needed. End with a summary: who was contacted, what was booked, who needs a call.",
"action_grants": [
{ "server_name": "Dojo", "tool_name": "book_makeup_class" },
{ "server_name": "Dojo", "tool_name": "send_parent_text" },
{ "server_name": "Dojo", "tool_name": "log_parent_message" }
]
}'
- action_grants are the write tools the run may call, one entry each.
mcp_server_idis optional and is looked up fromserver_name. Addconstraintsto pin an input field to a fixed value. For example,{"channel_id": "123"}locks a Discord post to one channel. A call with a different value is refused at call time. - read_tools (optional) is an explicit list of read tools,
[{ "server_name": "Dojo", "tool_name": "retention_report" }]. Once set, it replaces the automatic read-tool selection. Some tools are refused here and have to be granted instead:- tools annotated as writes (
readOnlyHint: falseordestructiveHint: true) - tools whose name starts with a write verb (
create,update,delete,send,post, …), unless annotatedreadOnlyHint: true
- tools annotated as writes (
- timezone is an IANA name. Without it, the crontab runs on Europe/Stockholm time.
The result includes pre_approved_tools: the exact tool list every run gets. Nothing else is offered to the agent. list_automations shows the same list later.
5. Dry-run and inspect
cogny tools call run_automation --input '{"automation_id":"<id>","dry_run":true}'
cogny tools call get_automation_run --input '{"ticket_id":"<ticket_id>"}'
A dry run has no write authority. The agent reads, then reports what it would have booked and sent. get_automation_run returns the run status, any error, and a condensed transcript of each tool call with its input and a preview of the result. Fix the endpoint or the instructions and dry-run again until the report reads right. Dry runs use credits like any run.
6. Run once for real, then enable
Show the user the dry-run summary and get their OK. Then:
cogny tools call run_automation --input '{"automation_id":"<id>","dry_run":false}'
cogny tools call update_automation --input '{"automation_id":"<id>","enabled":true}'
update_automation only changes the fields you pass. action_grants and read_tools replace the whole list, and read_tools: null goes back to automatic read-tool selection. Enabling needs a schedule: enabled: true fails with "Set a crontab before enabling" until the automation has a crontab. run_automation works without one.
What a run looks like
Every run, scheduled or run now, becomes a ticket on the Tickets board with its full transcript, so the user can see what the agent read, booked and sent. Runs use the workspace's credits like any agent run. When the workspace is out of credits, the run is skipped instead of starting. A schedule fires at most once an hour.
Common mistakes
- Wrong timezone.
0 10 * * 1without atimezoneruns at 10:00 Stockholm time, which is 04:00 in Indiana. A quiet-hours rule then refuses every text. Always pass the business's timezone. - Invisible read tools. A read tool named outside the prefix list and not annotated
readOnlyHint: trueis not offered. Checkread_onlyin theadd_custom_mcp_serverresult andpre_approved_toolsafter creating. - Consent only in the prompt. If the prompt is the only thing stopping a text to a family without consent, a misread prompt sends it. Enforce it in the write tool.
- Unpinned grants. A grant on a tool that takes a target (a channel, an account, a campaign) without
constraintslets the agent choose any target. Pin it. - Stale tool list. After deploying a new or renamed tool, run
refresh_mcp_discovery. Otherwise Cogny still has the old list and the new tool is missing from the run. - Automations off. If the automation tools don't appear, automations aren't switched on for the workspace. They're included with Cloud; contact Cogny support.
In the dashboard
Reports → Create new → New Automation creates a job with one allowed tool, plus one optional pinned field. It is created disabled, on a Monday-morning schedule in your browser's timezone. After that, the automation's Settings dialog has the schedule picker with a Timezone selector. It also lets you edit each grant's label and pinned fields, or remove extra grants. Run now on the automation page is a real run, the same as run_automation with dry_run: false. Use the tool for dry runs.
Adding more write tools, or setting read_tools, isn't in the dashboard. Do it through the in-app chat or with create_automation / update_automation over MCP or the CLI.
Related: Automations and content loops · Use Cogny from your own agent · Cogny MCP server
Reading this with an AI agent? Fetch the raw markdown at /docs/connect-your-own-app-to-automations/index.md or see llms.txt.