Build Composio
KINDAreplaces $29/mosaves $348/yrback to the verdict
A local MCP server exposing hand-written tools for the three or four services you actually use, where the hard parts are a transport that breaks on one stray print, credentials that must never reach the model, provider limits the agent cannot see, and write tools an impatient agent will call twice.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs the server and the test suite.
Get it nodejs.org, or nvm install 22. open ↗
Verify
node --version prints v22 or higher - installfree
Why The stdout discipline and the tool descriptions are only testable under a client that actually speaks the protocol.
Get it Claude Desktop, Cursor, or your own agent loop built on the SDK. open ↗
Verify
The client lists the smoke tool from phase one - API keyfree
Why Every tool is a wrapper around one of these, and a token with more permission than the tools use is exposure for nothing.
Get it A GitHub fine-grained token with an expiry and only the repositories and permissions the tools need, a Slack user token for your own workspace, a Notion integration token shared with only the pages you want read.
Verify
Each token lists exactly the permissions the tools call - have readyfree
Why Idempotency and dry run are tested by posting, and the first posts of a new write tool should land somewhere nobody reads.
Get it A private Slack channel, a scratch repository and a scratch Notion page.
Verify
A test write appears in each and nowhere else - decidefree
Why The verdict on this category is that under five hand-written integrations beat a hosted layer and above it they do not; deciding the list first keeps the build honest.
Get it Count the integrations you would call this month and write the list down.
Verify
The list exists and has fewer than five entries
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- providers(id, enabled, base_url, token_ref, scopes_declared, rate_policy_json) · what each token may do, written down - tools(name, provider_id, kind, input_schema, output_fields, description) · kind is read or write - calls(id, tool, at, duration_ms, status, provider_status, retry_after_s, bytes_returned) · shapes and statuses, never bodies - writes(idempotency_key, tool, at, provider_ref, result_json) · a write tool never repeats a key - budgets(provider_id, window_start, used, limit, unit) · from the provider's published limits, checked before the call - The calls log records what was asked and how the provider answered but never the content returned, because a log of everything the agent read is a second copy of your mailbox.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
PROVIDERS | required | github,slack,notion | Enabled providers. A missing token for any of these stops startup. |
GITHUB_TOKENsecret | optional | github_pat_...redacted | Fine-grained, least privilege, with an expiry. The doctor command reports when it lapses. |
SLACK_USER_TOKENsecret | optional | xoxp-...redacted | Your own workspace. History calls are budgeted at the published non-Marketplace rate. |
NOTION_TOKENsecret | optional | ntn_...redacted | An integration shared with only the pages the tools should see. |
LIMITS_PATH | required | ./config/limits.json | Per-provider budgets with the date each was checked. |
MAX_RESULT_BYTES | required | 16000 | Every read tool trims to this. The model's context is the scarce resource. |
WRITES_DB_PATH | required | ./data/writes.sqlite | Idempotency keys and results. Must survive a restart. |
LOG_LEVEL | required | info | Logs go to stderr only. There is no level that writes to stdout. |
The build, in order
A server that speaks only protocol on stdout
The server runs under a real client and nothing but MCP messages ever reaches stdout.
One stray print breaks the protocol; make it impossible rather than unlikely.
Files
src/index.tssrc/log.tstests/stdout-clean.test.tsNaming the variable, before any client connects.
Files
src/config.tsThe exact JSON block for two clients belongs in the README from day one.
Files
README.md
done when · tick each as it passeswatch out- A dependency that prints a deprecation notice to stdout, or one console.log left in a handler, corrupts the message stream and the client reports a parse error with no hint of the cause. Redirect all non-transport stdout writes to stderr at startup, and scan the stream in a test for anything that is not a newline-delimited JSON message.
Read tools that return what the model needs
Each read tool returns trimmed, bounded, paginated results with a description the model can act on.
The model's context is the scarce resource.
Files
src/providers/github.tssrc/providers/slack.tssrc/providers/notion.tsA bounded page beats a truncated dump.
Files
src/paging.tstests/output-size.test.tsTest with a canary token that must never appear.
Files
src/http.tstests/no-token-leak.test.ts
done when · tick each as it passeswatch out- Returning the provider's raw response hands the model forty kilobytes of metadata to find one subject line, and after three calls the conversation is out of context. Trim to the fields a person would read, cap the count, and return a cursor.
Limits the agent cannot see, made visible
Provider rate limits are known before the call and returned as a wait, never discovered as a ban.
They change; the config says when you last looked.
Files
config/limits.jsonsrc/budgets.tsTo the second. A guess is a longer wait.
Files
src/http.tstests/retry-after.test.tsThe agent can plan around a number; it cannot see a silent sleep.
Files
src/budgets.ts
done when · tick each as it passeswatch out- For Slack apps outside its Marketplace, message history calls are limited to one request per minute with at most fifteen results, so an agent paging through a channel hits the limit on its second call and, if the server retries for it, retries into a longer wait. Put the limit in the description, return the wait, and never retry a 429 more than once.
Write tools that cannot run twice
Sending, posting and creating happen once per intent, however many times the agent asks.
The model cannot omit what the schema demands.
Files
src/writes.tssrc/providers/slack.tsTested with a provider mock that counts calls.
Files
src/writes.tstests/idempotent-writes.test.tsWhat would be sent, verbatim, with no side effect.
Files
src/writes.ts
done when · tick each as it passeswatch out- An agent whose tool call timed out will call again with the same arguments, and without a key you have posted twice to a channel or opened two issues before anyone notices. Require the key in the schema, store it before calling, and return the stored result on a repeat without touching the provider.
Handing it to a client, and where the tokens liveproduct builder
Anyone with the README can connect a client, and a doctor command says what is wrong before the agent does.
Connected, expired or missing, per provider, with the fix.
Files
src/doctor.tsIn the client config and in the README.
Files
README.mdconfig/clients/A token that appears anywhere fails the build.
Files
tests/canary-scan.test.ts
done when · tick each as it passeswatch out- Pasting tokens into the client's configuration file means every screenshot, backup and support thread that includes it includes them. Reference the environment or the keychain from the config, and for a provider such as Gmail that has no personal token at all, say so: it needs an OAuth client, and one left in Google's Testing status issues refresh tokens that expire after seven days.
That is the whole plan for Composio. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- OAuth flows and app verification.
- Credentials for other people.
- Hundreds of connectors.
- An HTTP transport reachable beyond your machine.
- Hundreds of pre-built, pre-tested tool schemas you did not have to read API docs for
- Managed OAuth: app registration, consent screens, refresh tokens, revocation
- Per-end-user credential storage, which is the whole game if your agent serves anyone but you
- Someone else fixing your integration when a provider ships a breaking API change
- Usage observability, retries and rate limit smoothing across providers
- A per-tool audit view showing what the agent asked for this week, counts only, no bodies
- Provider-side search filters passed through, which cuts result sizes further than trimming
- A keychain adapter so tokens leave the environment file entirely
Need the files? The project pack on the verdict page hands your agent the whole brief · more dev tools.