mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-17 10:06:42 +02:00
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Connections give agents governed access to external services. > - Native connections should remain the first choice for a supported app. > - Other apps may be available through an external MCP provider. > - The user must know which external provider handles the connection and choose it before setup. > - This pull request adds ranked alternatives and server-authored instructions to connection search. > - Agents can follow the returned instructions while Paperclip validates saved choices and access. ## Linked Issues or Issue Description Related: #13879, which fixed inline MCP provider setup. This PR adds discovery and provider selection on top of that work. **Subsystem affected** Cross-cutting: shared connection contracts, server search and intent services, native runtime, CLI, inline setup UI, and evals. **Problem or motivation** An agent cannot offer a clear external-provider choice when Paperclip has no native connection for an app. Adding provider-specific branches to the core prompt would make those instructions harder to maintain. **Proposed solution** Prefer a native connection. Otherwise return verified alternatives in Composio, Arcade, Executor, Zapier order. Include an external-service disclosure, a question with None, and the next instruction in the search result. Validate the saved human choice before creating a selected fallback setup card. Reuse existing provider accounts and verify underlying app access separately. **Alternatives considered** Do not silently choose a provider. Do not claim that broad execution tools prove support for every app. Reuse existing questions and connection intents rather than add another connection model. **Roadmap alignment** Extends the existing MCP Tool Gateway & Apps and Agent evals & feedback capabilities. The MCP aggregators experiment remains the gate. No duplicate provider-routing PR was found in the public search. ## What Changed - Add a dated support index and authorized cached-tool evidence for external routes. - Return provider questions and next-step instructions from `connections_search`. - Preserve pending choices and declines across continuation. Validate company, task, agent, human, app, and current route eligibility. - Carry the selected app into new setup and account reuse, validate explicit provider requests against persisted human messages, and distinguish provider readiness from app authorization. - Sync native, MCP, REST, and CLI contracts. Keep core agent instructions provider-neutral. - Add production-component Storybooks, focused database tests, and three real-agent browser eval cases. - Record the plan, observed failures, fixes, passing evidence, and acceptance limits. ## Verification - Latest head `586f0e6cd`: 54 checks passed, 2 skipped; Greptile 5/5 and all review threads resolved. - After rebasing on master `18dac1e1e`: 64 focused shared, validator, route-contract, and database tests passed; server typecheck passed. - Embedded-browser test drive on the rebased head: native Jira card, HubSpot external-provider question, Arcade account reuse, one actual MCP read against a local synthetic fixture, reload persistence, and None preventing further calls. A real OpenAI-backed agent performed discovery and continuation. - UX observation: the agent initially combined mutually exclusive request fields; the server rejected it and the agent recovered without changing access. This extra retry remains visible in the transcript. - After rebase: 23 focused eval grader/catalog tests, affected TypeScript checks, token gates, production UI build, and Storybook build passed. Full local tests are intentionally excluded at the maintainer's request. - Before rebase: four browser/real-agent attempts passed: native Jira, None, and reuse of the second provider on two Codex profiles. - Browser evals used an isolated deterministic MCP fixture through the real Paperclip gateway. They do not prove production compatibility with all four providers. - Review `Apps / Connections / Provider choice` in Storybook. Choose Arcade, continue through Access, and verify the app name, external-service disclosure, and URL configuration. - The detailed verification report is `doc/connections/2026-09-23-aggregator-routing-verification.md`. ## Risks - The public support index is finite and can age. Account capability and app authorization still require verification after selection. - Existing installed-tool permissions remain in effect. Provider choice is not a new execution permission boundary. An early Mini attempt skipped search; clearer provider-neutral instructions made the targeted rerun pass. This is not a measured reliability rate. - Explicit requests skip provider confirmation only when a clear persisted human message or saved provider choice supports them. Other phrasing falls back to confirmation; the agent query alone is not consent. These routes do not add tool permissions. - No database migration or legacy Composio broker is added. Real-provider acceptance remains separate from fixture proof. ## Model Used OpenAI GPT-6 through Codex, with reasoning, repository tools, code execution, and browser tools. The exact deployment variant and context-window size are not exposed in this session. Product evals separately used the repository's primary Codex and Codex Mini profiles; those agents supplied test behavior, not independent provider compatibility proof. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
79 lines
3.1 KiB
TypeScript
79 lines
3.1 KiB
TypeScript
import { Command } from "commander";
|
||
import {
|
||
CONNECTION_INTENT_AGENT_GUIDANCE,
|
||
connectionRequestInputSchema,
|
||
connectionsSearchInputSchema,
|
||
} from "@paperclipai/shared";
|
||
|
||
interface RuntimeConnectionOptions {
|
||
retryProviderChoice?: boolean;
|
||
json?: boolean;
|
||
}
|
||
|
||
async function callRuntimeConnectionTool(
|
||
endpointEnv: "PAPERCLIP_RUNTIME_TOOLS_CONNECTIONS_SEARCH_URL" | "PAPERCLIP_RUNTIME_TOOLS_CONNECTION_REQUEST_URL",
|
||
body: unknown,
|
||
) {
|
||
const endpoint = process.env[endpointEnv]?.trim();
|
||
const token = process.env.PAPERCLIP_RUNTIME_TOOLS_TOKEN?.trim();
|
||
if (!endpoint || !token) {
|
||
throw new Error("This command requires the runtime connection environment from an active heartbeat run");
|
||
}
|
||
const response = await fetch(endpoint, {
|
||
method: "POST",
|
||
headers: {
|
||
Authorization: `Bearer ${token}`,
|
||
"Content-Type": "application/json",
|
||
Accept: "application/json",
|
||
},
|
||
body: JSON.stringify(body),
|
||
});
|
||
const text = await response.text();
|
||
const parsed = text ? JSON.parse(text) as unknown : null;
|
||
if (!response.ok) {
|
||
const message = parsed && typeof parsed === "object" && "error" in parsed
|
||
? String((parsed as { error: unknown }).error)
|
||
: `Runtime connection request failed with ${response.status}`;
|
||
throw new Error(message);
|
||
}
|
||
return parsed;
|
||
}
|
||
|
||
function writeResult(value: unknown, options: RuntimeConnectionOptions) {
|
||
process.stdout.write(`${JSON.stringify(value, null, options.json ? 2 : 0)}\n`);
|
||
}
|
||
|
||
export function registerConnectionIntentCommands(program: Command) {
|
||
const connections = program
|
||
.command("connections")
|
||
.description("Search or request connections from an active heartbeat run")
|
||
.addHelpText("after", `\n${CONNECTION_INTENT_AGENT_GUIDANCE}\n`);
|
||
|
||
connections
|
||
.command("search")
|
||
.argument("[query]", "Service name or capability")
|
||
.option("--retry-provider-choice", "Reconsider a provider choice only at the user’s explicit request")
|
||
.option("--json", "Print formatted JSON")
|
||
.action(async (query: string | undefined, options: RuntimeConnectionOptions) => {
|
||
const input = connectionsSearchInputSchema.parse({ query: query ?? "", retryProviderChoice: options.retryProviderChoice });
|
||
writeResult(await callRuntimeConnectionTool(
|
||
"PAPERCLIP_RUNTIME_TOOLS_CONNECTIONS_SEARCH_URL",
|
||
input,
|
||
), options);
|
||
});
|
||
|
||
connections
|
||
.command("request")
|
||
.argument("<service>", "Connectable service slug")
|
||
.option("--target-service <slug>", "Requested app when the user explicitly names an external provider")
|
||
.option("--selection-interaction-id <id>", "Answered external-provider question ID")
|
||
.option("--json", "Print formatted JSON")
|
||
.action(async (service: string, options: RuntimeConnectionOptions & { targetService?: string }) => {
|
||
const input = connectionRequestInputSchema.parse({ service, targetService: options.targetService, selectionInteractionId: (options as RuntimeConnectionOptions & { selectionInteractionId?: string }).selectionInteractionId });
|
||
writeResult(await callRuntimeConnectionTool(
|
||
"PAPERCLIP_RUNTIME_TOOLS_CONNECTION_REQUEST_URL",
|
||
input,
|
||
), options);
|
||
});
|
||
}
|