onairos-text-auth-mcp
v0.1.0
Published
MCP server and agent bridge for Onairos text authorization — six onairos_* tools for LLM agents behind Linq message threads, built on onairos-text-auth.
Readme
onairos-text-auth-mcp
MCP server + agent bridge for the onairos-text-auth SDK — for Linq apps with an LLM agent behind the thread.
The split: your agent decides when, deterministic code decides how. The agent pitches, detects opt-ins, and fetches data through MCP tools. The bridge runs the actual auth flow — verification codes and consent replies never route through the model, so your agent structurally cannot forge a consent.
Quickstart
import express from 'express';
import { createTextAuthRelay } from 'onairos-text-auth';
import { createTextAuthAgentBridge, buildTextAuthMcpServer } from 'onairos-text-auth-mcp';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
const app = express();
app.use(express.json({ verify: (req, res, buf) => { req.rawBody = buf; } }));
const relay = createTextAuthRelay({
onairosApiKey: process.env.ONAIROS_API_KEY,
webhookSecret: process.env.LINQ_WEBHOOK_SECRET,
appName: 'My App',
requestedConfirmations: ['personality', 'preferences'],
autoExchangeGrants: true
});
const bridge = createTextAuthAgentBridge({
relay,
sendMessage: ({ chatId, text }) => myLinqClient.send(chatId, text)
});
// 1) Bridge first on every webhook — active-flow messages bypass the agent.
app.post('/linq/webhook', async (req, res) => {
const incoming = await bridge.handleIncoming({
headers: req.headers, rawBody: req.rawBody, body: req.body
});
if (!incoming.ok) return res.status(401).json({ ok: false });
if (incoming.handled) return res.json({ ok: true });
await runAgentTurn(incoming.message); // your LLM, with the MCP tools below
res.json({ ok: true });
});
// 2) MCP server in the same process, sharing the bridge state.
const mcpServer = buildTextAuthMcpServer({ bridge });
app.post('/mcp', async (req, res) => {
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
res.on('close', () => transport.close());
await mcpServer.connect(transport);
await transport.handleRequest(req, res, req.body);
});The six tools your agent gets
| Tool | Purpose |
|---|---|
| onairos_start_connect | Start the flow after a genuine opt-in — once per opt-in |
| onairos_list_accounts | Which platforms the user connected (silent — nothing sent to the chat) |
| onairos_flow_status | Flow active? Authorized? Data fetchable? |
| onairos_get_data | The personalization payload — consented scopes only |
| onairos_request_authorization | Ask for (re-)authorization; the user still replies YES themselves |
| onairos_disconnect | Revoke grants when the user asks to stop sharing |
Guarantees
- Flow messages (email, verification code, YES/NO) are forwarded by the bridge before your agent sees anything — they never enter the model's context.
- Tools can only trigger flow-start and informational commands; consent injection is rejected server-side.
- Replayed command bodies are stripped of
event_id/event_type, so agent-triggered actions never collide with webhook dedup. - Data handoffs are cached ~50 minutes, then re-exchanged automatically from stored grants (valid 90 days).
Multi-process deployments
The default state store is in-memory (single process). If your MCP server runs separately from your webhook handler, pass a shared store ({ get, set, delete }, e.g. Redis-backed) to createTextAuthAgentBridge.
License
Apache-2.0
