Write June MCP programs
How a June MCP program finds and calls your integrations' tools, three worked examples, what comes back, and its limits.
The execute tool runs one short program against your integrations and hands its result to your client. Your client usually writes the program for you; this page is for when you write one yourself, or want to know why one failed.
How a program runs
A program is TypeScript (or plain JavaScript) that runs on June's side, in its own small runtime. It is the body of an async function: await works at the top, and return gives the result.
toolsreaches your integrations. It is the only way out:fetchis switched off, and there are no files, packages or commands.console.log,console.info,console.warn,console.errorandconsole.debugwrite lines that come back with the result.- It is not June's sandbox: your Environment packages and the sandbox's files are not there.
Find a tool
Ask tools.search in plain words. It ranks the tools of every connection you have and returns the best 12:
const found = await tools.search({ query: "search mail" });
return found.items.map((tool) => tool.path);Each item has a path, the tool's name, its description, its integration and a score. Pass limit for more, and offset with the nextOffset of the last answer for the next page.
To list every tool of one integration, pass its name as both namespace and query: tools.search({ namespace: "<integration>", query: "<integration>" }). With an empty query, search returns nothing.
Before you call a tool, read what it takes:
return await tools.describe.tool({ path: "june_github.user.default.get_file" });The answer has the input as a TypeScript type (inputTypeScript) and the output's type. A path that does not exist answers with suggestions instead.
Call a tool
A tool's path is its address under tools:
await tools.<integration>.<owner>.<connection>.<tool>(input)<integration>is the name search reports, such asjune_github.<owner>isuserfor a connection you signed in to, andorgfor an MCP server you added that needs no sign-in. Both are yours: June MCP never reaches an organization's connections, whatever the address says.<connection>is the connection's name:default, unless you gave it one such aswork. With two accounts on one service, each has its own address.
A call that works answers { ok: true, data }, with the tool's answer in data. A tool's type also allows { ok: false, error: { code, message } }, so check ok before you read data:
const file = await tools.june_github.user.default.get_file({ owner: "acme", repo: "website", path: "README.md" });
if (!file.ok) return file.error;
return file.data.content;A call that fails almost always throws instead, with Internal tool error and a short id but not the reason: a path with no tool, a wrong input, a missing file and an expired sign-in look the same. Check the path and the input with tools.describe.tool first, and catch errors where one failing call should not end the program.
Examples
Each example uses the default connection; use the address tools.search gives you for yours.
Find unread mail from someone
const found = await tools.june_email.user.default.search_messages({
from: "accountant@acmestudio.com",
unread: true,
after: "2026-10-01",
limit: 20,
});
if (!found.ok) return found.error;
return found.data.messages.map(({ date, from, subject }) => ({ date, from, subject }));search_messages also filters by to, subject, text and before, and returns at most 50 summaries at a time, with a nextCursor to pass as cursor for the next page. To read one message in full, pass its id to get_message. Outlook mail has the same tools under the Microsoft 365 integration, june_microsoft.
Read a Google Drive document
const drive = tools.june_google_drive.user.default;
const found = await drive.search_files({ query: "Q3 plan", limit: 5 });
if (!found.ok) return found.error;
const [file] = found.data.files;
if (!file) return "No file matches.";
const read = await drive.read_file({ fileId: file.id, maxCharacters: 20000 });
if (!read.ok) return read.error;
return {
name: file.name,
link: file.webViewLink,
truncated: read.data.truncated,
text: read.data.content,
};truncated says whether the document went on past maxCharacters. A Google Sheet is read with read_sheet instead, one range of rows at a time (nextRange names the rows after it); see Google Drive.
Read a file from a GitHub repository
const github = tools.june_github.user.default;
const results: Record<string, unknown> = {};
for (const repo of ["website", "api"]) {
try {
const details = await github.get_repository({ owner: "acme", repo });
if (!details.ok) {
results[repo] = details.error;
continue;
}
const readme = await github.get_file({ owner: "acme", repo, path: "README.md" });
if (!readme.ok) {
results[repo] = readme.error;
continue;
}
results[repo] = {
branch: details.data.defaultBranch,
readme: readme.data.content.slice(0, 2000),
};
} catch (error) {
results[repo] = String(error);
}
}
return results;get_file reads one text file of up to 100 KB, from the default branch unless you pass ref, a branch, tag or commit. A client reaches GitHub only through these reads: there is no listing of repositories, issues or pull requests.
What comes back
- The result: what the program returns. A string comes back as it is; anything else as indented JSON.
- What it emitted: after the result, under
Emitted:, one line peremit(value): a string as it is, anything else as JSON, and an image or a file by its type, such as[image image/png]. - Steps that need your approval: next, under
Needs the user's approval, which nothing can ask for yet:, each such call with its tool and exact input (see below). - The logs: last, under
Logs:, each line with its level in front, such as[log] 12 messages. - An error: if the program throws or stops, the client gets the error, with the logs below it, and no result.
- Credentials stay out: a text field named
token,secret,passwordorapi_key(alsoapikey), or ending in_token,_secret,_passwordor_api_key, such asaccess_tokenorclient_secret, comes back as[REDACTED], whatever it held. So doBearervalues, and pairs such astoken=…orpassword=…in text. Other spellings, such asaccessToken, are not caught.
Limits
| Limit | Value | Beyond it |
|---|---|---|
| Program | 20,000 characters | the call is refused before it runs |
| Run time | 60 seconds, counting the time spent waiting for tools | the program stops and the call answers with a timeout error |
| Memory | 64 MB | the program stops with an error |
| Result | 64 KB, measured as JSON | the result is cut at 64 KB and ends in ...[TRUNCATED]; it then arrives as text, no longer valid JSON |
| Logs | the first 200 lines, each up to 4,000 characters, 64,000 bytes in all | later lines are dropped and a longer line is cut |
| Emitted | the same as logs | the same as logs |
| Steps that need approval | the first 20, 64 KB in all | the rest are counted on one line, such as - 5 more, not listed |
Return only the fields the client needs, such as subjects rather than whole messages. For a long file, cut the text in the program or ask the tool for less: Google Drive's read_file takes maxCharacters (50,000 by default).
Missing connections and approvals
An integration you have not connected has no tools to find. skills names the MCP servers you added on the Integrations page but have not signed in to, and the client can give you a June link to connect an integration.
A step that needs your approval, such as creating a Microsoft 365 calendar event, sending a Teams message or a tool an MCP server marks that way, does not run: the call throws an error that names the tool and says it needs your approval and did not run, and the answer lists the call, with its exact input, under Needs the user's approval. An MCP server's tool that asks you a question while it runs is told no; what it does then is up to the server, and it is not listed. Ask June for these steps in a chat on the web or in the app, where you approve each one on a card (how).
What June does not do
- It does not keep anything between programs: each call to
executestarts fresh.
Set up your client
Add June MCP to Claude Code, Codex, Cursor, OpenCode or any client that adds remote MCP servers with a sign-in, then sign in with your June account.
Security and privacy
Who sees your chats, where your connections' passwords and tokens go, what reaches the model, and what share links expose.