feat(apps): expand the self-serve connection catalog (#12344)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - A useful app store needs accurate and selectable provider definitions. > - Local brand assets now cover the expanded provider set. > - Provider methods differ in transport, authentication, ownership, and required scope. > - This pull request expands the catalog and encodes those provider contracts. > - The benefit is a larger self-serve store with explicit setup choices. ## Linked Issues or Issue Description Refs #11965 This is stack 6 of 11. It depends on stack 5 and replaces another reviewable part of #11965. ## What Changed - Add and update provider definitions for the self-serve catalog. - Add Google Workspace connection methods and capability profiles. - Add catalog generation, ingestion, URL matching, and contract tests. - Update legacy key tests to use a provider that still uses header credentials. ## Verification - `pnpm --filter @paperclipai/server typecheck` - `pnpm --filter @paperclipai/server exec vitest run src/__tests__/tool-access-service.test.ts` - Result: 164 tests passed. - `pnpm build` ## Risks - An incorrect provider definition can offer the wrong setup method. - Contract tests verify transport, authentication, and provider URL behavior. - The change does not add a database migration. > I checked `ROADMAP.md`. This stack continues the existing app connection work from #11965 and does not duplicate another planned item. ## Model Used OpenAI Codex, GPT-5. The runtime model ID and context window were not exposed. The model used reasoning, tool use, and code execution. ## 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 `Refs #` or (b) described the issue in this pull request - [x] I have not referenced internal or instance-local Paperclip issues or links - [x] My branch name describes the change 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>
This commit is contained in:
parent
10c4902ffc
commit
fcb2e99e8f
|
|
@ -18,9 +18,19 @@ describe("tool app gallery URL matching", () => {
|
|||
expect(getAppDefinitionForUrl("https://docs.googleapis.com/drive/v3/files")).toBeNull();
|
||||
});
|
||||
|
||||
it("does not list Google Drive until its OAuth client flow is supported", () => {
|
||||
expect(CONNECTABLE_APP_DEFINITIONS.map((app) => app.slug)).not.toContain("google-drive");
|
||||
expect(getAppDefinitionForUrl("https://mcp.google.com/drive")).toBeNull();
|
||||
it("lists each reviewed Google Workspace MCP endpoint independently", () => {
|
||||
expect(CONNECTABLE_APP_DEFINITIONS.map((app) => app.slug)).toEqual(expect.arrayContaining([
|
||||
"gmail",
|
||||
"google-drive",
|
||||
"google-docs",
|
||||
"google-sheets",
|
||||
"google-slides",
|
||||
"google-calendar",
|
||||
"google-chat",
|
||||
"google-people",
|
||||
"google-workspace-search",
|
||||
]));
|
||||
expect(getAppDefinitionForUrl("https://drivemcp.googleapis.com/mcp/v1")?.slug).toBe("google-drive");
|
||||
});
|
||||
|
||||
it("lists Composio as a connectable API-key app", () => {
|
||||
|
|
|
|||
|
|
@ -4,14 +4,61 @@ import a2 from "./app-definitions/slack.json" with { type: "json" };
|
|||
import a3 from "./app-definitions/notion.json" with { type: "json" };
|
||||
import a4 from "./app-definitions/posthog.json" with { type: "json" };
|
||||
import a5 from "./app-definitions/linear.json" with { type: "json" };
|
||||
import a6 from "./app-definitions/google-sheets.json" with { type: "json" };
|
||||
import a7 from "./app-definitions/context7.json" with { type: "json" };
|
||||
import a6 from "./app-definitions/context7.json" with { type: "json" };
|
||||
import a7 from "./app-definitions/shopify.json" with { type: "json" };
|
||||
import a8 from "./app-definitions/composio.json" with { type: "json" };
|
||||
import a9 from "./app-definitions/oauth-generic.json" with { type: "json" };
|
||||
import a10 from "./app-definitions/api-key-generic.json" with { type: "json" };
|
||||
import a11 from "./app-definitions/sentry.json" with { type: "json" };
|
||||
import a12 from "./app-definitions/vercel.json" with { type: "json" };
|
||||
import a13 from "./app-definitions/anthropic.json" with { type: "json" };
|
||||
import a14 from "./app-definitions/gmail.json" with { type: "json" };
|
||||
import a14 from "./app-definitions/jira.json" with { type: "json" };
|
||||
import a15 from "./app-definitions/airtable.json" with { type: "json" };
|
||||
import a16 from "./app-definitions/beehiiv.json" with { type: "json" };
|
||||
import a17 from "./app-definitions/bitly.json" with { type: "json" };
|
||||
import a18 from "./app-definitions/candid.json" with { type: "json" };
|
||||
import a19 from "./app-definitions/cloudflare.json" with { type: "json" };
|
||||
import a20 from "./app-definitions/cloudinary.json" with { type: "json" };
|
||||
import a21 from "./app-definitions/coda.json" with { type: "json" };
|
||||
import a22 from "./app-definitions/hugging-face.json" with { type: "json" };
|
||||
import a23 from "./app-definitions/kernel.json" with { type: "json" };
|
||||
import a24 from "./app-definitions/local-falcon.json" with { type: "json" };
|
||||
import a25 from "./app-definitions/make.json" with { type: "json" };
|
||||
import a26 from "./app-definitions/manufact.json" with { type: "json" };
|
||||
import a27 from "./app-definitions/miro.json" with { type: "json" };
|
||||
import a28 from "./app-definitions/netlify.json" with { type: "json" };
|
||||
import a29 from "./app-definitions/oreilly.json" with { type: "json" };
|
||||
import a30 from "./app-definitions/planetscale.json" with { type: "json" };
|
||||
import a31 from "./app-definitions/resend.json" with { type: "json" };
|
||||
import a32 from "./app-definitions/ticktick.json" with { type: "json" };
|
||||
import a33 from "./app-definitions/todoist.json" with { type: "json" };
|
||||
import a34 from "./app-definitions/webflow.json" with { type: "json" };
|
||||
import a35 from "./app-definitions/wix.json" with { type: "json" };
|
||||
import a36 from "./app-definitions/brex.json" with { type: "json" };
|
||||
import a37 from "./app-definitions/clickhouse.json" with { type: "json" };
|
||||
import a38 from "./app-definitions/egnyte.json" with { type: "json" };
|
||||
import a39 from "./app-definitions/embat.json" with { type: "json" };
|
||||
import a40 from "./app-definitions/mixpanel.json" with { type: "json" };
|
||||
import a41 from "./app-definitions/postman.json" with { type: "json" };
|
||||
import a42 from "./app-definitions/razorpay.json" with { type: "json" };
|
||||
import a43 from "./app-definitions/sanity.json" with { type: "json" };
|
||||
import a44 from "./app-definitions/stripe.json" with { type: "json" };
|
||||
import a45 from "./app-definitions/supabase.json" with { type: "json" };
|
||||
import a46 from "./app-definitions/ticket-tailor.json" with { type: "json" };
|
||||
import a47 from "./app-definitions/asana.json" with { type: "json" };
|
||||
import a48 from "./app-definitions/box.json" with { type: "json" };
|
||||
import a49 from "./app-definitions/mem0.json" with { type: "json" };
|
||||
import a50 from "./app-definitions/pagerduty.json" with { type: "json" };
|
||||
import a51 from "./app-definitions/similarweb.json" with { type: "json" };
|
||||
import a52 from "./app-definitions/xero.json" with { type: "json" };
|
||||
import a53 from "./app-definitions/gmail.json" with { type: "json" };
|
||||
import a54 from "./app-definitions/google-drive.json" with { type: "json" };
|
||||
import a55 from "./app-definitions/google-docs.json" with { type: "json" };
|
||||
import a56 from "./app-definitions/google-sheets.json" with { type: "json" };
|
||||
import a57 from "./app-definitions/google-slides.json" with { type: "json" };
|
||||
import a58 from "./app-definitions/google-calendar.json" with { type: "json" };
|
||||
import a59 from "./app-definitions/google-chat.json" with { type: "json" };
|
||||
import a60 from "./app-definitions/google-people.json" with { type: "json" };
|
||||
import a61 from "./app-definitions/google-workspace-search.json" with { type: "json" };
|
||||
import type { AppDefinition } from "./types/app-definition.js";
|
||||
export const APP_DEFINITIONS=[a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14] as AppDefinition[];
|
||||
export const APP_DEFINITIONS=[a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31,a32,a33,a34,a35,a36,a37,a38,a39,a40,a41,a42,a43,a44,a45,a46,a47,a48,a49,a50,a51,a52,a53,a54,a55,a56,a57,a58,a59,a60,a61] as AppDefinition[];
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,18 +1,200 @@
|
|||
import { describe,expect,it } from "vitest";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { APP_DEFINITIONS } from "./app-definitions.generated.js";
|
||||
import { recommendedDefaultsForApp } from "./app-definitions.js";
|
||||
import { APP_STORE_DEFINITIONS, APP_STORE_HIDDEN_SLUGS, CONNECTABLE_APP_DEFINITIONS, appSupportsCatalogSetup, getAvailableConnectionMethod, getRecommendedConnectionMethod, recommendedDefaultsForApp, resolveConnectionMethodServerUrl } from "./app-definitions.js";
|
||||
import { BLOCKED_MCP_PROVIDERS, SELF_SERVE_MCP_CANDIDATES, SELF_SERVE_MCP_RESEARCH } from "./self-serve-mcp-research.js";
|
||||
import { appDefinitionsSchema } from "./validators/app-definition.js";
|
||||
describe("AppDefinition catalog",()=>{
|
||||
it("validates all Wave 1 definitions",()=>expect(()=>appDefinitionsSchema.parse(APP_DEFINITIONS)).not.toThrow());
|
||||
it("contains the reviewed providers",()=>expect(APP_DEFINITIONS.map((app)=>app.slug)).toEqual(["zapier","github","slack","notion","posthog","linear","google-sheets","context7","composio","oauth-generic","api-key-generic","sentry","vercel","anthropic","gmail"]));
|
||||
it("contains every established provider plus the reviewed self-serve catalog",()=>{
|
||||
expect(APP_DEFINITIONS.map((app)=>app.slug)).toEqual(expect.arrayContaining(["zapier","github","slack","notion","posthog","linear","google-sheets","context7","composio","oauth-generic","api-key-generic","sentry","vercel","anthropic","gmail","google-drive","google-docs","google-slides","google-calendar","google-chat","google-people","google-workspace-search"]));
|
||||
expect(SELF_SERVE_MCP_CANDIDATES).toHaveLength(43);
|
||||
expect(BLOCKED_MCP_PROVIDERS.map((entry)=>entry.slug)).toEqual(["g2","vercel","zomato"]);
|
||||
const definitionSlugs=new Set(APP_DEFINITIONS.map((app)=>app.slug));
|
||||
const connectableSlugs=new Set(CONNECTABLE_APP_DEFINITIONS.map((app)=>app.slug));
|
||||
expect(SELF_SERVE_MCP_CANDIDATES.filter((entry)=>!definitionSlugs.has(entry.slug))).toEqual([]);
|
||||
expect(SELF_SERVE_MCP_CANDIDATES.filter((entry)=>!connectableSlugs.has(entry.slug))).toEqual([]);
|
||||
for(const entry of BLOCKED_MCP_PROVIDERS)expect(connectableSlugs.has(entry.slug)).toBe(false);
|
||||
});
|
||||
it("keeps a complete, unique, dated evidence ledger for all 46 researched MCP providers",()=>{
|
||||
expect(SELF_SERVE_MCP_RESEARCH.verifiedAt).toBe("2026-08-26");
|
||||
expect(SELF_SERVE_MCP_RESEARCH.entries).toHaveLength(46);
|
||||
expect(new Set(SELF_SERVE_MCP_RESEARCH.entries.map((entry)=>entry.slug))).toHaveProperty("size",46);
|
||||
for(const entry of SELF_SERVE_MCP_RESEARCH.entries){
|
||||
expect(new URL(entry.docsUrl).protocol).toBe("https:");
|
||||
expect(new URL(entry.serverUrl).protocol).toBe("https:");
|
||||
expect(entry.authMode).toBeTruthy();
|
||||
expect(entry.prerequisite.length).toBeGreaterThan(10);
|
||||
expect(["S1","S2","S3","S4"]).toContain(entry.riskTier);
|
||||
}
|
||||
});
|
||||
it("uses the reviewed current endpoints and configuration modes",()=>{
|
||||
const method=(slug:string,key?:string)=>APP_DEFINITIONS.find((app)=>app.slug===slug)?.methods.find((candidate)=>!key||candidate.key===key);
|
||||
expect(method("jira")?.defaults?.serverUrl).toBe("https://mcp.atlassian.com/v1/mcp/authv2");
|
||||
expect(method("jira")?.defaults?.scopesHint).toEqual([
|
||||
"read:me",
|
||||
"read:account",
|
||||
"offline_access",
|
||||
"email",
|
||||
"read:jira-work",
|
||||
"write:jira-work",
|
||||
"search:confluence",
|
||||
"read:confluence-user",
|
||||
"read:page:confluence",
|
||||
"write:page:confluence",
|
||||
"read:comment:confluence",
|
||||
"write:comment:confluence",
|
||||
"read:space:confluence",
|
||||
"read:hierarchical-content:confluence",
|
||||
"write:component:compass",
|
||||
"read:component:compass",
|
||||
"read:scorecard:compass",
|
||||
"write:scorecard:compass",
|
||||
"read:event:compass",
|
||||
"read:metric:compass",
|
||||
"read:all:twg",
|
||||
"write:all:twg",
|
||||
]);
|
||||
expect(method("cloudinary")?.defaults?.serverUrl).toBe("https://asset-management.mcp.cloudinary.com/mcp");
|
||||
expect(method("kernel")?.defaults?.serverUrl).toBe("https://mcp.onkernel.com/mcp");
|
||||
expect(method("resend")?.defaults?.serverUrl).toBe("https://mcp.resend.com/mcp");
|
||||
expect(method("clickhouse")?.defaults?.serverUrl).toBe("https://mcp.clickhouse.cloud/clickstack");
|
||||
expect(method("clickhouse")?.tenantFields?.[0]?.transport).toEqual({location:"header",name:"x-service-id"});
|
||||
expect(method("mem0")).toMatchObject({auth:"api_key",keyPlacement:{location:"header",name:"Authorization",prefix:"Bearer "}});
|
||||
expect(method("mem0")?.defaults?.serverUrl).toBe("https://mcp.mem0.ai/mcp/");
|
||||
expect(method("xero")?.defaults?.scopesHint).toEqual([
|
||||
"openid","profile","email","offline_access","accounting.settings","accounting.invoices.read","accounting.reports.aged.read","accounting.reports.balancesheet.read","accounting.reports.profitandloss.read",
|
||||
]);
|
||||
expect(APP_DEFINITIONS.find((app)=>app.slug==="pagerduty")?.methods.map((candidate)=>({key:candidate.key,serverUrl:candidate.defaults?.serverUrl}))).toEqual([
|
||||
{key:"mcp-api-key-us",serverUrl:"https://mcp.pagerduty.com/mcp"},
|
||||
{key:"mcp-api-key-eu",serverUrl:"https://mcp.eu.pagerduty.com/mcp"},
|
||||
]);
|
||||
expect(method("context7")).toMatchObject({auth:"none",defaults:{serverUrl:"https://mcp.context7.com/mcp"}});
|
||||
expect(APP_DEFINITIONS.find((app)=>app.slug==="planetscale")?.methods.map((candidate)=>candidate.key)).toEqual(["mcp-oauth","mcp-insights-only"]);
|
||||
expect(APP_DEFINITIONS.find((app)=>app.slug==="postman")?.methods.map((candidate)=>candidate.key)).toEqual([
|
||||
"mcp-oauth-minimal","mcp-oauth-code","mcp-oauth-full","mcp-eu-key-minimal","mcp-eu-key-code","mcp-eu-key-full",
|
||||
]);
|
||||
expect(method("supabase")?.tenantFields?.find((field)=>field.key==="readOnly")?.defaultValue).toBe(false);
|
||||
expect(method("asana")?.ownershipModes).toEqual(["customer"]);
|
||||
expect(method("zapier")).toMatchObject({key:"generated-url",auth:"none",defaults:{}});
|
||||
expect(method("zapier")?.credentialFields).toBeUndefined();
|
||||
});
|
||||
it("uses discovery-first Notion MCP OAuth metadata",()=>{
|
||||
const notion=APP_DEFINITIONS.find((app)=>app.slug==="notion");
|
||||
expect(notion?.redirectConstraints).toBe("https-or-loopback-http");
|
||||
expect(notion?.methods[0]?.defaults).toEqual({serverUrl:"https://mcp.notion.com/mcp"});
|
||||
});
|
||||
it("preserves required Linear OAuth scopes",()=>expect(APP_DEFINITIONS.find((app)=>app.slug==="linear")?.methods[0]?.defaults?.scopesHint).toEqual(["read","write"]));
|
||||
it("requests only Hugging Face's MCP read scope",()=>expect(APP_DEFINITIONS.find((app)=>app.slug==="hugging-face")?.methods[0]?.defaults?.scopesHint).toEqual(["read-mcp"]));
|
||||
it("defaults S2-S4 write and destructive actions to ask-first",()=>{for(const app of APP_DEFINITIONS)for(const method of app.methods)expect(recommendedDefaultsForApp(app,method.key)).toEqual({access:"all_agents",askFirstRiskLevels:method.riskTier==="S1"?[]:["write","destructive"]})});
|
||||
it("keeps Gmail personal-only and bound to the Paperclip ID broker",()=>expect(APP_DEFINITIONS.find((app)=>app.slug==="gmail")?.methods[0]).toMatchObject({oauthStrategy:"paperclip_id_connector",grantKinds:["user"],defaults:{serverUrl:"https://gmailmcp.googleapis.com/mcp/v1",scopesHint:["https://www.googleapis.com/auth/gmail.readonly","https://www.googleapis.com/auth/gmail.compose"]}}));
|
||||
it("offers PostHog OAuth and API-key methods with broad defaults and advanced narrowing",()=>{const posthog=APP_DEFINITIONS.find((app)=>app.slug==="posthog");expect(posthog?.methods.map((method)=>method.key)).toEqual(["mcp-oauth","mcp-api-key"]);for(const method of posthog?.methods??[]){expect(method.riskTier).toBe("S3");expect(method.tenantFields?.find((field)=>field.key==="readOnly")?.defaultValue).toBe(false);expect(method.tenantFields?.find((field)=>field.key==="projectId")?.transport).toEqual({location:"header",name:"x-posthog-project-id"});expect(method.tenantFields?.filter((field)=>field.advanced).map((field)=>field.key)).toEqual(["features","tools","mode"]);expect(method.configRequirements).toBeUndefined();expect(method.requiredResourceFilters).toEqual(["project"])}});
|
||||
it("defaults explicit read/write capability groups to their write-capable method",()=>{
|
||||
const drive=APP_DEFINITIONS.find((app)=>app.slug==="google-drive")!;
|
||||
const gmail=APP_DEFINITIONS.find((app)=>app.slug==="gmail")!;
|
||||
expect(getAvailableConnectionMethod(drive)?.key).toBe("customer-write-oauth");
|
||||
expect(getAvailableConnectionMethod(gmail)?.key).toBe("customer-draft-oauth");
|
||||
expect(getRecommendedConnectionMethod(drive.methods.filter((candidate)=>candidate.ownershipModes.includes("customer")))?.key).toBe("customer-write-oauth");
|
||||
});
|
||||
it("explains Google Workspace Developer Preview enrollment before connection",()=>{
|
||||
const googleWorkspaceMcpSlugs=["gmail","google-drive","google-docs","google-slides","google-calendar","google-chat","google-people","google-workspace-search"];
|
||||
for(const slug of googleWorkspaceMcpSlugs){
|
||||
const prerequisite=APP_DEFINITIONS.find((app)=>app.slug===slug)?.setupPrerequisite;
|
||||
expect(prerequisite?.actionUrl,slug).toBe("https://developers.google.com/workspace/preview");
|
||||
expect(prerequisite?.description,slug).toContain("does not enable unrelated Paperclip customers");
|
||||
expect(prerequisite?.steps?.join(" "),slug).toContain("final project-registration email");
|
||||
}
|
||||
});
|
||||
it("withholds unverified and reserved providers from the app store without deleting their definitions",()=>{
|
||||
expect([...APP_STORE_HIDDEN_SLUGS].sort()).toEqual([
|
||||
"beehiiv","bitly","brex","candid","coda","composio","context7","egnyte","embat","github","kernel","local-falcon","make","manufact","oreilly","planetscale","razorpay","sanity","similarweb","slack","ticket-tailor","ticktick","xero",
|
||||
]);
|
||||
expect(APP_STORE_DEFINITIONS).toHaveLength(35);
|
||||
const connectableSlugs=new Set(CONNECTABLE_APP_DEFINITIONS.map((entry)=>entry.slug));
|
||||
const storeSlugs=new Set(APP_STORE_DEFINITIONS.map((entry)=>entry.slug));
|
||||
for(const slug of APP_STORE_HIDDEN_SLUGS){
|
||||
expect(connectableSlugs.has(slug),slug).toBe(true);
|
||||
expect(storeSlugs.has(slug),slug).toBe(false);
|
||||
}
|
||||
});
|
||||
it("ships complete local branding provenance for all 35 store-visible providers",()=>{
|
||||
const uiPublic=path.resolve(path.dirname(fileURLToPath(import.meta.url)),"../../../ui/public");
|
||||
const manifest=JSON.parse(fs.readFileSync(path.join(uiPublic,"brands/apps/manifest.json"),"utf8")) as {providers:Array<{slug:string;catalogVisible:boolean;localAsset:string;darkAsset?:string;officialSourceUrl:string;upstreamAssetUrl:string;assetType:"svg"|"png";darkVariantRequired:boolean}>};
|
||||
const visible=manifest.providers.filter((entry)=>entry.catalogVisible);
|
||||
expect(visible).toHaveLength(35);
|
||||
expect(new Set(visible.map((entry)=>entry.slug))).toHaveProperty("size",35);
|
||||
expect(new Set(visible.map((entry)=>entry.localAsset))).toHaveProperty("size",35);
|
||||
expect(new Set(APP_STORE_DEFINITIONS.map((entry)=>entry.slug))).toEqual(new Set(visible.map((entry)=>entry.slug)));
|
||||
for(const app of APP_STORE_DEFINITIONS){
|
||||
const provenance=visible.find((entry)=>entry.slug===app.slug)!;
|
||||
expect(provenance).toBeTruthy();
|
||||
expect(provenance.localAsset).toBe(app.branding.logoUrl);
|
||||
expect(provenance.darkAsset).toBe(app.branding.darkLogoUrl);
|
||||
expect(provenance.darkVariantRequired).toBe(Boolean(provenance.darkAsset));
|
||||
expect(new URL(provenance.officialSourceUrl).protocol).toBe("https:");
|
||||
expect(new URL(provenance.upstreamAssetUrl).protocol).toBe("https:");
|
||||
expect(provenance.localAsset).toMatch(/^\/brands\/apps\/.+\.(svg|png)$/);
|
||||
expect(provenance.localAsset).not.toContain("google.com/s2/favicons");
|
||||
const asset=fs.readFileSync(path.join(uiPublic,provenance.localAsset));
|
||||
if(provenance.assetType==="png"){
|
||||
expect(asset.subarray(0,8).toString("hex")).toBe("89504e470d0a1a0a");
|
||||
expect(asset.readUInt32BE(16)).toBeGreaterThanOrEqual(128);
|
||||
expect(asset.readUInt32BE(20)).toBeGreaterThanOrEqual(128);
|
||||
}else{
|
||||
const svg=asset.toString("utf8");
|
||||
expect(svg).toMatch(/^<svg\b/);
|
||||
expect(svg).not.toMatch(/<script|<foreignObject|\son[a-z]+\s*=/i);
|
||||
}
|
||||
if(provenance.darkAsset)expect(fs.existsSync(path.join(uiPublic,provenance.darkAsset))).toBe(true);
|
||||
}
|
||||
});
|
||||
it("keeps every researched self-serve candidate implemented while blocked providers stay absent",()=>{
|
||||
const definitions=new Map(CONNECTABLE_APP_DEFINITIONS.map((entry)=>[entry.slug,entry]));
|
||||
for(const candidate of SELF_SERVE_MCP_CANDIDATES)expect(appSupportsCatalogSetup(definitions.get(candidate.slug))).toBe(true);
|
||||
for(const blocked of BLOCKED_MCP_PROVIDERS)expect(definitions.has(blocked.slug)).toBe(false);
|
||||
});
|
||||
it("ships each Google Workspace surface as an independent personal OAuth app",()=>{
|
||||
const expected={
|
||||
gmail:"https://gmailmcp.googleapis.com/mcp/v1",
|
||||
"google-drive":"https://drivemcp.googleapis.com/mcp/v1",
|
||||
"google-docs":"https://docsmcp.googleapis.com/mcp/v1",
|
||||
"google-sheets":"https://sheetsmcp.googleapis.com/mcp/v1",
|
||||
"google-slides":"https://slidesmcp.googleapis.com/mcp/v1",
|
||||
"google-calendar":"https://calendarmcp.googleapis.com/mcp/v1",
|
||||
"google-chat":"https://chatmcp.googleapis.com/mcp/v1",
|
||||
"google-people":"https://people.googleapis.com/mcp/v1",
|
||||
"google-workspace-search":"https://workspacemcp.googleapis.com/mcp/v1",
|
||||
} as const;
|
||||
for(const [slug,serverUrl] of Object.entries(expected)){
|
||||
const app=APP_DEFINITIONS.find((candidate)=>candidate.slug===slug);
|
||||
expect(app,slug).toBeTruthy();
|
||||
expect(app?.methods.some((method)=>method.oauthStrategy==="paperclip_id_connector")).toBe(true);
|
||||
for(const method of app?.methods.filter((candidate)=>candidate.auth==="oauth")??[]){
|
||||
expect(method.grantKinds,`${slug}:${method.key}`).toEqual(["user"]);
|
||||
expect(method.defaults?.serverUrl,`${slug}:${method.key}`).toBe(serverUrl);
|
||||
expect(method.capabilityProfile,`${slug}:${method.key}`).toBeTruthy();
|
||||
}
|
||||
}
|
||||
});
|
||||
it("does not advertise a managed Gmail read-only method before profile-scoped connector support",()=>{
|
||||
const gmail=APP_DEFINITIONS.find((app)=>app.slug==="gmail");
|
||||
const managedMethods=gmail?.methods.filter((method)=>method.oauthStrategy==="paperclip_id_connector")??[];
|
||||
expect(managedMethods.map((method)=>method.key)).toEqual(["paperclip-draft"]);
|
||||
expect(managedMethods[0]?.defaults?.scopesHint).toEqual([
|
||||
"https://www.googleapis.com/auth/gmail.readonly",
|
||||
"https://www.googleapis.com/auth/gmail.compose",
|
||||
]);
|
||||
});
|
||||
it("configures Shopify's official tenant-scoped Storefront MCP without OAuth",()=>{const method=APP_DEFINITIONS.find((app)=>app.slug==="shopify")?.methods[0];expect(method).toMatchObject({key:"storefront-mcp",auth:"none",defaults:{serverUrlTemplate:"https://{storeDomain}/api/mcp"},tenantFields:[expect.objectContaining({key:"storeDomain",required:true})]});expect(resolveConnectionMethodServerUrl(method!,{storeDomain:"paperclip-demo.myshopify.com"})).toBe("https://paperclip-demo.myshopify.com/api/mcp");expect(resolveConnectionMethodServerUrl(method!,{})).toBeNull()});
|
||||
it("offers PostHog OAuth and API-key methods with zero-config defaults and advanced narrowing",()=>{const posthog=APP_DEFINITIONS.find((app)=>app.slug==="posthog");expect(posthog?.methods.map((method)=>method.key)).toEqual(["mcp-oauth","mcp-api-key"]);for(const method of posthog?.methods??[]){const projectField=method.tenantFields?.find((field)=>field.key==="projectId");expect(method.riskTier).toBe("S3");expect(method.tenantFields?.find((field)=>field.key==="readOnly")).toMatchObject({defaultValue:false,advanced:true});expect(projectField).toMatchObject({advanced:true,transport:{location:"header",name:"x-posthog-project-id"}});expect(projectField?.required).not.toBe(true);expect(method.tenantFields?.filter((field)=>field.advanced).map((field)=>field.key)).toEqual(["projectId","readOnly","features","tools"]);expect(method.tenantFields?.find((field)=>field.key==="mode")).toMatchObject({hidden:true,defaultValue:"tools",transport:{location:"query",name:"mode"}});expect(method.configRequirements).toBeUndefined();expect(method.requiredResourceFilters).toBeUndefined();expect(method.guidanceMd).toContain("optional advanced controls")}});
|
||||
it("requires only reviewed provider or safety-boundary configuration on the default path",()=>{const required=APP_DEFINITIONS.flatMap((app)=>app.methods.flatMap((method)=>[...(method.tenantFields??[]),...(method.extensionFields??[])].filter((field)=>field.required&&field.advanced!==true&&!field.hidden).map((field)=>`${app.slug}:${method.key}:${field.key}`))).sort();expect(required).toEqual(["clickhouse:mcp-oauth:serviceId","shopify:storefront-mcp:storeDomain","supabase:mcp-api-key:projectRef","supabase:mcp-oauth:projectRef"])});
|
||||
it("limits Vercel Connect setup to the reviewed pilot methods",()=>{
|
||||
const reviewed=APP_DEFINITIONS.flatMap((app)=>app.methods.flatMap((method)=>method.credentialSources?.vercelConnect?[{slug:app.slug,key:method.key,review:method.credentialSources.vercelConnect}]:[]));
|
||||
expect(reviewed.map(({slug,key})=>`${slug}:${key}`).sort()).toEqual(["linear:mcp-oauth","notion:mcp-oauth","posthog:mcp-api-key","posthog:mcp-oauth"]);
|
||||
expect(reviewed.find(({slug})=>slug==="linear")?.review).toMatchObject({services:["linear"],principalModes:["user"],scopes:["read","write"],header:{name:"Authorization",prefix:"Bearer "}});
|
||||
expect(reviewed.find(({slug,key})=>slug==="posthog"&&key==="mcp-oauth")?.review.services).toEqual(["posthog","mcp.posthog.com/mcp"]);
|
||||
expect(reviewed.find(({slug,key})=>slug==="posthog"&&key==="mcp-api-key")?.review.principalModes).toEqual(["app"]);
|
||||
expect(APP_DEFINITIONS.find((app)=>app.slug==="vercel")?.availability?.available).toBe(false);
|
||||
});
|
||||
it("enforces method and field invariants",()=>{for(const app of APP_DEFINITIONS)for(const method of app.methods){if(method.auth==="api_key")expect(method.keyPlacement).toBeTruthy();if(method.auth==="oauth")expect(method.ownershipModes.length).toBeGreaterThan(0);for(const field of method.credentialFields??[])if(field.required&&field.type!=="checkbox")expect(field.placeholder).toBeTruthy()}});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import { APP_DEFINITIONS } from "./app-definitions.generated.js";
|
||||
import { SELF_SERVE_MCP_CANDIDATES } from "./self-serve-mcp-research.js";
|
||||
import type { AppDefinition, ConnectionMethodDef, FieldDef } from "./types/app-definition.js";
|
||||
import type { ToolConnectionOwnership } from "./types/tool-access.js";
|
||||
|
||||
const CONNECTABLE_APP_SLUGS = new Set([
|
||||
export const CONNECTABLE_APP_SLUGS = new Set([
|
||||
...SELF_SERVE_MCP_CANDIDATES.map((entry) => entry.slug),
|
||||
"zapier",
|
||||
"github",
|
||||
"slack",
|
||||
|
|
@ -11,14 +13,58 @@ const CONNECTABLE_APP_SLUGS = new Set([
|
|||
"linear",
|
||||
"google-sheets",
|
||||
"context7",
|
||||
"shopify",
|
||||
"composio",
|
||||
"gmail",
|
||||
"google-drive",
|
||||
"google-docs",
|
||||
"google-slides",
|
||||
"google-calendar",
|
||||
"google-chat",
|
||||
"google-people",
|
||||
"google-workspace-search",
|
||||
]);
|
||||
|
||||
export const CONNECTABLE_APP_DEFINITIONS = APP_DEFINITIONS.filter((app) =>
|
||||
CONNECTABLE_APP_SLUGS.has(app.slug)
|
||||
);
|
||||
|
||||
/**
|
||||
* Definitions retained for existing connections and later verification, but
|
||||
* intentionally withheld from the customer-facing store. Keeping visibility
|
||||
* separate from recognition avoids breaking saved connections when a provider
|
||||
* is pulled from Browse or reserved for a future first-party experience.
|
||||
*/
|
||||
export const APP_STORE_HIDDEN_SLUGS = new Set([
|
||||
"beehiiv",
|
||||
"bitly",
|
||||
"brex",
|
||||
"candid",
|
||||
"coda",
|
||||
"composio",
|
||||
"context7",
|
||||
"egnyte",
|
||||
"embat",
|
||||
"github",
|
||||
"kernel",
|
||||
"local-falcon",
|
||||
"make",
|
||||
"manufact",
|
||||
"oreilly",
|
||||
"planetscale",
|
||||
"razorpay",
|
||||
"sanity",
|
||||
"similarweb",
|
||||
"slack",
|
||||
"ticket-tailor",
|
||||
"ticktick",
|
||||
"xero",
|
||||
]);
|
||||
|
||||
export const APP_STORE_DEFINITIONS = CONNECTABLE_APP_DEFINITIONS.filter((app) =>
|
||||
!APP_STORE_HIDDEN_SLUGS.has(app.slug)
|
||||
);
|
||||
|
||||
export const DEFAULT_OWNERSHIP_AVAILABILITY: Record<ToolConnectionOwnership, boolean> = {
|
||||
platform_shared: false,
|
||||
platform_provisioned: false,
|
||||
|
|
@ -30,6 +76,14 @@ export function getConnectableAppDefinition(slug: string): AppDefinition | null
|
|||
return CONNECTABLE_APP_DEFINITIONS.find((app) => app.slug === slug) ?? null;
|
||||
}
|
||||
|
||||
export function getAppStoreDefinition(slug: string): AppDefinition | null {
|
||||
return APP_STORE_DEFINITIONS.find((app) => app.slug === slug) ?? null;
|
||||
}
|
||||
|
||||
export function isAppStoreVisibleSlug(slug: string | null | undefined): boolean {
|
||||
return Boolean(slug && !APP_STORE_HIDDEN_SLUGS.has(slug) && CONNECTABLE_APP_SLUGS.has(slug));
|
||||
}
|
||||
|
||||
function wildcardPatternToRegExp(pattern: string): RegExp {
|
||||
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
||||
return new RegExp(`^${escaped}$`, "i");
|
||||
|
|
@ -57,22 +111,125 @@ export function getAvailableConnectionMethods(app: AppDefinition): ConnectionMet
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick the method that gives a new connection the app's useful write surface.
|
||||
*
|
||||
* Google Workspace publishes separate read and write capability groups. The
|
||||
* read method is intentionally listed first for documentation, but treating
|
||||
* array order as a product default silently created read-only connections and
|
||||
* left every write action Off. Capability metadata is the durable signal; apps
|
||||
* without an explicit write/draft capability retain their declared order.
|
||||
*/
|
||||
export function getRecommendedConnectionMethod(
|
||||
methods: readonly ConnectionMethodDef[],
|
||||
): ConnectionMethodDef | null {
|
||||
return methods.find((method) => {
|
||||
const capabilityKey = method.capabilityProfile?.key;
|
||||
return capabilityKey === "write" || capabilityKey === "draft";
|
||||
}) ?? methods[0] ?? null;
|
||||
}
|
||||
|
||||
export function getAvailableConnectionMethod(
|
||||
app: AppDefinition,
|
||||
methodKey?: string | null,
|
||||
): ConnectionMethodDef | null {
|
||||
const methods = getAvailableConnectionMethods(app);
|
||||
return methodKey ? methods.find((method) => method.key === methodKey) ?? null : methods[0] ?? null;
|
||||
return methodKey
|
||||
? methods.find((method) => method.key === methodKey) ?? null
|
||||
: getRecommendedConnectionMethod(methods);
|
||||
}
|
||||
|
||||
export function connectionMethodSupportsAutomaticOAuth(method: ConnectionMethodDef | null | undefined): boolean {
|
||||
return method?.auth === "oauth" && (
|
||||
method.oauthStrategy === "paperclip_id_connector"
|
||||
|| method.ownershipModes.includes("dcr")
|
||||
);
|
||||
}
|
||||
|
||||
export function connectionMethodAcceptsCustomerOAuthClient(method: ConnectionMethodDef | null | undefined): boolean {
|
||||
return method?.auth === "oauth"
|
||||
&& !method.oauthStrategy
|
||||
&& method.ownershipModes.includes("customer");
|
||||
}
|
||||
|
||||
export function connectionMethodSupportsCatalogSetup(method: ConnectionMethodDef | null | undefined): boolean {
|
||||
if (!method) return false;
|
||||
if (method.auth === "none" || method.auth === "api_key") return true;
|
||||
return connectionMethodSupportsAutomaticOAuth(method)
|
||||
|| connectionMethodAcceptsCustomerOAuthClient(method);
|
||||
}
|
||||
|
||||
export function connectionMethodRequiresConfiguration(method: ConnectionMethodDef | null | undefined): boolean {
|
||||
if (!method) return false;
|
||||
const visibleTenantFields = method.tenantFields?.filter((field) => !field.hidden) ?? [];
|
||||
const visibleExtensionFields = method.extensionFields?.filter((field) => !field.hidden) ?? [];
|
||||
return Boolean(
|
||||
method.credentialFields?.length
|
||||
|| visibleTenantFields.length
|
||||
|| visibleExtensionFields.length
|
||||
|| method.configRequirements?.atLeastOneOf?.length
|
||||
// "Use your own OAuth app" is an advanced alternative when DCR/CIMD is
|
||||
// available, not a required setup field. Only customer-client-only methods
|
||||
// must stop on the configuration screen.
|
||||
|| (
|
||||
connectionMethodAcceptsCustomerOAuthClient(method)
|
||||
&& !connectionMethodSupportsAutomaticOAuth(method)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function appSupportsCatalogSetup(app: AppDefinition | null | undefined): boolean {
|
||||
return Boolean(app && getAvailableConnectionMethods(app).some(connectionMethodSupportsCatalogSetup));
|
||||
}
|
||||
|
||||
export function isConnectableAppSlug(slug: string | null | undefined): boolean {
|
||||
return Boolean(slug && CONNECTABLE_APP_SLUGS.has(slug));
|
||||
}
|
||||
|
||||
export function appSupportsAutomaticOAuth(app: AppDefinition | null | undefined): boolean {
|
||||
return Boolean(app && getAvailableConnectionMethods(app).some(connectionMethodSupportsAutomaticOAuth));
|
||||
}
|
||||
|
||||
export function appAcceptsCustomerOAuthClient(app: AppDefinition | null | undefined): boolean {
|
||||
return Boolean(app && getAvailableConnectionMethods(app).some(connectionMethodAcceptsCustomerOAuthClient));
|
||||
}
|
||||
|
||||
export function credentialConfigPath(field: FieldDef): string {
|
||||
return `credentials.${field.key}`;
|
||||
}
|
||||
|
||||
export function resolveConnectionMethodServerUrl(
|
||||
method: ConnectionMethodDef,
|
||||
configValues: Record<string, string | boolean>,
|
||||
): string | null {
|
||||
const template = method.defaults?.serverUrlTemplate;
|
||||
if (!template) return method.defaults?.serverUrl ?? null;
|
||||
|
||||
let missingValue = false;
|
||||
const resolved = template.replace(/\{([a-zA-Z0-9_-]+)\}/g, (_placeholder, key: string) => {
|
||||
const value = configValues[key];
|
||||
if (value === undefined || String(value).trim().length === 0) {
|
||||
missingValue = true;
|
||||
return "";
|
||||
}
|
||||
return encodeURIComponent(String(value).trim());
|
||||
});
|
||||
if (missingValue) return null;
|
||||
|
||||
try {
|
||||
return new URL(resolved).toString();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function recommendedDefaultsForApp(app: AppDefinition, methodKey?: string | null): Record<string, unknown> {
|
||||
const method = getAvailableConnectionMethod(app, methodKey);
|
||||
const normalizedMethodKey = app.slug === "gmail" && methodKey === "paperclip-id-oauth" ? "paperclip-draft" : methodKey;
|
||||
const method = normalizedMethodKey
|
||||
? app.methods.find((candidate) => candidate.key === normalizedMethodKey) ?? null
|
||||
: getAvailableConnectionMethod(app, null);
|
||||
return {
|
||||
access: "all_agents",
|
||||
askFirstRiskLevels: method?.riskTier === "S1" ? [] : ["write", "destructive"],
|
||||
askFirstRiskLevels: method && method.riskTier !== "S1" ? ["write", "destructive"] : [],
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "airtable",
|
||||
"name": "Airtable",
|
||||
"description": "Connect Airtable's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"data"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/airtable.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.airtable.com/*"
|
||||
],
|
||||
"docsUrl": "https://support.airtable.com/articles/9897799762-using-the-airtable-mcp-server",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.airtable.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Airtable in the browser. An Airtable account; enterprise administrators may need to allowlist the client.",
|
||||
"riskTier": "S3",
|
||||
"label": "Sign in with Airtable",
|
||||
"consoleLinks": {
|
||||
"docs": "https://support.airtable.com/articles/9897799762-using-the-airtable-mcp-server"
|
||||
},
|
||||
"warnings": [
|
||||
"An Airtable account; enterprise administrators may need to allowlist the client."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -8,7 +8,8 @@
|
|||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "https://www.google.com/s2/favicons?domain=anthropic.com&sz=128"
|
||||
"logoUrl": "/brands/apps/anthropic.svg",
|
||||
"darkLogoUrl": "/brands/apps/anthropic-dark.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://api.anthropic.com/*"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "https://www.google.com/s2/favicons?domain=openapis.org&sz=128"
|
||||
"logoUrl": "/brands/apps/api-key-generic.svg"
|
||||
},
|
||||
"urlPatterns": [],
|
||||
"methods": [
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "asana",
|
||||
"name": "Asana",
|
||||
"description": "Connect Asana's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"productivity"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/asana.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.asana.com/*"
|
||||
],
|
||||
"docsUrl": "https://developers.asana.com/docs/integrating-with-asanas-mcp-server",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-own-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Register an OAuth app with Asana, then enter its client ID and secret.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.asana.com/v2/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Asana in the browser. Create an Asana MCP OAuth app and register Paperclip's callback URI; DCR is not supported.",
|
||||
"riskTier": "S3",
|
||||
"label": "Use your own OAuth app",
|
||||
"consoleLinks": {
|
||||
"register": "https://developers.asana.com/docs/integrating-with-asanas-mcp-server",
|
||||
"docs": "https://developers.asana.com/docs/integrating-with-asanas-mcp-server"
|
||||
},
|
||||
"warnings": [
|
||||
"Create an Asana MCP OAuth app and register Paperclip's callback URI; DCR is not supported."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "beehiiv",
|
||||
"name": "beehiiv",
|
||||
"description": "Connect beehiiv's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"content"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/beehiiv.png"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.beehiiv.com/*"
|
||||
],
|
||||
"docsUrl": "https://www.beehiiv.com/features/mcp/getting-started",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.beehiiv.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect beehiiv in the browser. A beehiiv account; the subscription plan controls available write capabilities.",
|
||||
"riskTier": "S3",
|
||||
"label": "Sign in with beehiiv",
|
||||
"consoleLinks": {
|
||||
"docs": "https://www.beehiiv.com/features/mcp/getting-started"
|
||||
},
|
||||
"warnings": [
|
||||
"A beehiiv account; the subscription plan controls available write capabilities."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "bitly",
|
||||
"name": "Bitly",
|
||||
"description": "Connect Bitly's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"analytics"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/bitly.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://api-ssl.bitly.com/*"
|
||||
],
|
||||
"docsUrl": "https://dev.bitly.com/bitly-mcp/overview/quickstart/",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://api-ssl.bitly.com/v4/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Bitly in the browser. A Bitly account with either browser authorization or an API token.",
|
||||
"riskTier": "S2",
|
||||
"label": "Sign in with Bitly",
|
||||
"consoleLinks": {
|
||||
"docs": "https://dev.bitly.com/bitly-mcp/overview/quickstart/"
|
||||
},
|
||||
"warnings": [
|
||||
"A Bitly account with either browser authorization or an API token."
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "mcp-api-key",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "api_key",
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a restricted customer-owned key when browser sign-in is not suitable.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://api-ssl.bitly.com/v4/mcp"
|
||||
},
|
||||
"guidanceMd": "Use a customer-created Bitly key. A Bitly account with either browser authorization or an API token.",
|
||||
"riskTier": "S2",
|
||||
"label": "Use an API key",
|
||||
"credentialFields": [
|
||||
{
|
||||
"key": "authorization",
|
||||
"label": "Bitly API key",
|
||||
"type": "password",
|
||||
"required": true,
|
||||
"placeholder": "Paste your Bitly API token",
|
||||
"secret": true
|
||||
}
|
||||
],
|
||||
"keyPlacement": {
|
||||
"location": "header",
|
||||
"name": "Authorization",
|
||||
"prefix": "Bearer "
|
||||
},
|
||||
"consoleLinks": {
|
||||
"keys": "https://dev.bitly.com/bitly-mcp/overview/quickstart/",
|
||||
"docs": "https://dev.bitly.com/bitly-mcp/overview/quickstart/"
|
||||
},
|
||||
"warnings": [
|
||||
"A Bitly account with either browser authorization or an API token."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "box",
|
||||
"name": "Box",
|
||||
"description": "Connect Box's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"content"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/box.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.box.com/*"
|
||||
],
|
||||
"docsUrl": "https://support.box.com/hc/en-us/articles/43847256139923-Managing-Box-MCP-Servers",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-own-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Register an OAuth app with Box, then enter its client ID and secret.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.box.com"
|
||||
},
|
||||
"guidanceMd": "Connect Box in the browser. A Box administrator creates the OAuth integration and enables AI access.",
|
||||
"riskTier": "S3",
|
||||
"label": "Use your own OAuth app",
|
||||
"consoleLinks": {
|
||||
"register": "https://support.box.com/hc/en-us/articles/43847256139923-Managing-Box-MCP-Servers",
|
||||
"docs": "https://support.box.com/hc/en-us/articles/43847256139923-Managing-Box-MCP-Servers"
|
||||
},
|
||||
"warnings": [
|
||||
"A Box administrator creates the OAuth integration and enables AI access."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "brex",
|
||||
"name": "Brex",
|
||||
"description": "Connect Brex's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"commerce"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/brex.svg",
|
||||
"darkLogoUrl": "/brands/apps/brex-dark.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://api.brex.com/*"
|
||||
],
|
||||
"docsUrl": "https://www.brex.com/support/using-brex-in-ai-apps",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://api.brex.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Brex in the browser. Brex early access and an administrator enabling the integration; financial actions require explicit approval.",
|
||||
"riskTier": "S4",
|
||||
"label": "Sign in with Brex",
|
||||
"consoleLinks": {
|
||||
"docs": "https://www.brex.com/support/using-brex-in-ai-apps"
|
||||
},
|
||||
"warnings": [
|
||||
"Brex early access and an administrator enabling the integration; financial actions require explicit approval.",
|
||||
"Financial or destructive actions must be explicitly approved before execution."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "candid",
|
||||
"name": "Candid",
|
||||
"description": "Connect Candid's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"data"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/candid.png"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.candid.org/*"
|
||||
],
|
||||
"docsUrl": "https://learning.candid.org/getting-started-with-the-candid-mcp-connector/375441",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.candid.org/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Candid in the browser. A Candid account with access to the MCP connector.",
|
||||
"riskTier": "S2",
|
||||
"label": "Sign in with Candid",
|
||||
"consoleLinks": {
|
||||
"docs": "https://learning.candid.org/getting-started-with-the-candid-mcp-connector/375441"
|
||||
},
|
||||
"warnings": [
|
||||
"A Candid account with access to the MCP connector."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "clickhouse",
|
||||
"name": "ClickHouse",
|
||||
"description": "Connect ClickHouse's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"data"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/clickhouse.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.clickhouse.cloud/*"
|
||||
],
|
||||
"docsUrl": "https://clickhouse.com/blog/announcing-managed-clickstack-mcp-server",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.clickhouse.cloud/clickstack"
|
||||
},
|
||||
"guidanceMd": "Connect ClickHouse in the browser. A ClickHouse Cloud ClickStack service and its service ID.",
|
||||
"riskTier": "S4",
|
||||
"label": "Sign in with ClickHouse",
|
||||
"consoleLinks": {
|
||||
"docs": "https://clickhouse.com/blog/announcing-managed-clickstack-mcp-server"
|
||||
},
|
||||
"warnings": [
|
||||
"A ClickHouse Cloud ClickStack service and its service ID."
|
||||
],
|
||||
"tenantFields": [
|
||||
{
|
||||
"key": "serviceId",
|
||||
"label": "ClickHouse Cloud service ID",
|
||||
"type": "text",
|
||||
"required": true,
|
||||
"placeholder": "11e1031f-9a13-4cac-9bc7-d4ec9286ec17",
|
||||
"helperMd": "Copy the service ID from ClickStack → Team Settings → API & Agents.",
|
||||
"transport": {
|
||||
"location": "header",
|
||||
"name": "x-service-id"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requiredResourceFilters": [
|
||||
"service"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "cloudflare",
|
||||
"name": "Cloudflare",
|
||||
"description": "Connect Cloudflare's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"developer"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/cloudflare.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.cloudflare.com/*"
|
||||
],
|
||||
"docsUrl": "https://developers.cloudflare.com/agents/model-context-protocol/cloudflare/servers-for-cloudflare/",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.cloudflare.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Cloudflare in the browser. A Cloudflare account with access to the resources being connected.",
|
||||
"riskTier": "S3",
|
||||
"label": "Sign in with Cloudflare",
|
||||
"consoleLinks": {
|
||||
"docs": "https://developers.cloudflare.com/agents/model-context-protocol/cloudflare/servers-for-cloudflare/"
|
||||
},
|
||||
"warnings": [
|
||||
"A Cloudflare account with access to the resources being connected."
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "mcp-api-key",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "api_key",
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a restricted customer-owned key when browser sign-in is not suitable.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.cloudflare.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Use a customer-created Cloudflare key. A Cloudflare account with access to the resources being connected.",
|
||||
"riskTier": "S3",
|
||||
"label": "Use an API key",
|
||||
"credentialFields": [
|
||||
{
|
||||
"key": "authorization",
|
||||
"label": "Cloudflare API key",
|
||||
"type": "password",
|
||||
"required": true,
|
||||
"placeholder": "Paste your Cloudflare API token",
|
||||
"secret": true
|
||||
}
|
||||
],
|
||||
"keyPlacement": {
|
||||
"location": "header",
|
||||
"name": "Authorization",
|
||||
"prefix": "Bearer "
|
||||
},
|
||||
"consoleLinks": {
|
||||
"keys": "https://developers.cloudflare.com/agents/model-context-protocol/cloudflare/servers-for-cloudflare/",
|
||||
"docs": "https://developers.cloudflare.com/agents/model-context-protocol/cloudflare/servers-for-cloudflare/"
|
||||
},
|
||||
"warnings": [
|
||||
"A Cloudflare account with access to the resources being connected."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "cloudinary",
|
||||
"name": "Cloudinary",
|
||||
"description": "Connect Cloudinary's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"content"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/cloudinary.svg",
|
||||
"darkLogoUrl": "/brands/apps/cloudinary-dark.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://asset-management.mcp.cloudinary.com/*"
|
||||
],
|
||||
"docsUrl": "https://cloudinary.com/documentation/cloudinary_llm_mcp",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://asset-management.mcp.cloudinary.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Cloudinary in the browser. A Cloudinary account; authorization is limited by the signed-in user's roles.",
|
||||
"riskTier": "S3",
|
||||
"label": "Sign in with Cloudinary",
|
||||
"consoleLinks": {
|
||||
"docs": "https://cloudinary.com/documentation/cloudinary_llm_mcp"
|
||||
},
|
||||
"warnings": [
|
||||
"A Cloudinary account; authorization is limited by the signed-in user's roles."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "coda",
|
||||
"name": "Coda",
|
||||
"description": "Connect Coda's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"productivity"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/coda.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://coda.io/*"
|
||||
],
|
||||
"docsUrl": "https://help.coda.io/hc/en-us/articles/44722769665549-Security-recommendations-for-the-Coda-MCP",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://coda.io/apis/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Coda in the browser. A Coda account; the hosted MCP service is currently beta.",
|
||||
"riskTier": "S3",
|
||||
"label": "Sign in with Coda",
|
||||
"consoleLinks": {
|
||||
"docs": "https://help.coda.io/hc/en-us/articles/44722769665549-Security-recommendations-for-the-Coda-MCP"
|
||||
},
|
||||
"warnings": [
|
||||
"A Coda account; the hosted MCP service is currently beta.",
|
||||
"This provider's hosted MCP server is currently beta or preview."
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "mcp-api-key",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "api_key",
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a restricted customer-owned key when browser sign-in is not suitable.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://coda.io/apis/mcp"
|
||||
},
|
||||
"guidanceMd": "Use a customer-created Coda key. A Coda account; the hosted MCP service is currently beta.",
|
||||
"riskTier": "S3",
|
||||
"label": "Use an API key",
|
||||
"credentialFields": [
|
||||
{
|
||||
"key": "authorization",
|
||||
"label": "Coda API key",
|
||||
"type": "password",
|
||||
"required": true,
|
||||
"placeholder": "Paste your Coda API token",
|
||||
"secret": true
|
||||
}
|
||||
],
|
||||
"keyPlacement": {
|
||||
"location": "header",
|
||||
"name": "Authorization",
|
||||
"prefix": "Bearer "
|
||||
},
|
||||
"consoleLinks": {
|
||||
"keys": "https://help.coda.io/hc/en-us/articles/44722769665549-Security-recommendations-for-the-Coda-MCP",
|
||||
"docs": "https://help.coda.io/hc/en-us/articles/44722769665549-Security-recommendations-for-the-Coda-MCP"
|
||||
},
|
||||
"warnings": [
|
||||
"A Coda account; the hosted MCP service is currently beta.",
|
||||
"This provider's hosted MCP server is currently beta or preview."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -3,18 +3,25 @@
|
|||
"slug": "composio",
|
||||
"name": "Composio",
|
||||
"description": "Connect Composio so Paperclip can discover and manage the toolkits in your project.",
|
||||
"categories": ["productivity"],
|
||||
"categories": [
|
||||
"productivity"
|
||||
],
|
||||
"featured": true,
|
||||
"branding": {
|
||||
"logoUrl": "https://www.google.com/s2/favicons?domain=composio.dev&sz=128"
|
||||
"logoUrl": "/brands/apps/composio.svg",
|
||||
"darkLogoUrl": "/brands/apps/composio-dark.svg"
|
||||
},
|
||||
"urlPatterns": ["https://backend.composio.dev/*"],
|
||||
"urlPatterns": [
|
||||
"https://backend.composio.dev/*"
|
||||
],
|
||||
"methods": [
|
||||
{
|
||||
"key": "api-key",
|
||||
"transport": "rest_api",
|
||||
"auth": "api_key",
|
||||
"ownershipModes": ["customer"],
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a project API key from the Composio project that owns the toolkits and connected accounts.",
|
||||
"defaults": {
|
||||
"serviceHost": "backend.composio.dev"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@
|
|||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "https://www.google.com/s2/favicons?domain=context7.com&sz=128"
|
||||
"logoUrl": "/brands/apps/context7.svg",
|
||||
"darkLogoUrl": "/brands/apps/context7-dark.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.context7.com/*"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "egnyte",
|
||||
"name": "Egnyte",
|
||||
"description": "Connect Egnyte's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"content"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/egnyte.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp-server.egnyte.com/*"
|
||||
],
|
||||
"docsUrl": "https://developers.egnyte.com/docs/Remote_MCP_Server",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp-server.egnyte.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Egnyte in the browser. An eligible Egnyte plan and administrator approval for external LLM access.",
|
||||
"riskTier": "S3",
|
||||
"label": "Sign in with Egnyte",
|
||||
"consoleLinks": {
|
||||
"docs": "https://developers.egnyte.com/docs/Remote_MCP_Server"
|
||||
},
|
||||
"warnings": [
|
||||
"An eligible Egnyte plan and administrator approval for external LLM access."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "embat",
|
||||
"name": "Embat",
|
||||
"description": "Connect Embat's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"commerce"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/embat.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://tellme.embat.io/*"
|
||||
],
|
||||
"docsUrl": "https://tellme.embat.io/.well-known/oauth-protected-resource/mcp",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://tellme.embat.io/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Embat in the browser. An Embat account; pilot the connection because provider setup documentation is sparse.",
|
||||
"riskTier": "S4",
|
||||
"label": "Sign in with Embat",
|
||||
"consoleLinks": {
|
||||
"docs": "https://tellme.embat.io/.well-known/oauth-protected-resource/mcp"
|
||||
},
|
||||
"warnings": [
|
||||
"An Embat account; pilot the connection because provider setup documentation is sparse."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -8,7 +8,8 @@
|
|||
],
|
||||
"featured": true,
|
||||
"branding": {
|
||||
"logoUrl": "https://www.google.com/s2/favicons?domain=github.com&sz=128"
|
||||
"logoUrl": "/brands/apps/github.svg",
|
||||
"darkLogoUrl": "/brands/apps/github-dark.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://api.githubcopilot.com/mcp/*"
|
||||
|
|
|
|||
|
|
@ -3,24 +3,90 @@
|
|||
"slug": "gmail",
|
||||
"name": "Gmail",
|
||||
"description": "Search and read Gmail messages and create drafts without enabling mail sending.",
|
||||
"categories": ["communication", "productivity"],
|
||||
"categories": [
|
||||
"communication",
|
||||
"productivity"
|
||||
],
|
||||
"featured": true,
|
||||
"branding": {
|
||||
"logoUrl": "https://www.google.com/s2/favicons?domain=gmail.com&sz=128"
|
||||
"logoUrl": "/brands/apps/gmail.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://gmailmcp.googleapis.com/*"
|
||||
],
|
||||
"docsUrl": "https://developers.google.com/workspace/gmail/api/reference/mcp",
|
||||
"setupPrerequisite": {
|
||||
"title": "Google Developer Preview access required",
|
||||
"description": "Google must register both the Workspace email used to authorize Paperclip and the Google Cloud project that owns the OAuth client. Registration is limited to those emails and projects; it does not enable unrelated Paperclip customers.",
|
||||
"steps": [
|
||||
"Apply with the Workspace email that will sign in and the Google Cloud project that owns the OAuth client.",
|
||||
"Wait for the Google Group notification and then Google's final project-registration email, usually within a couple of days.",
|
||||
"Add every additional tester email or Cloud project through Google's member request forms before connecting."
|
||||
],
|
||||
"actionLabel": "Apply or verify Developer Preview enrollment",
|
||||
"actionUrl": "https://developers.google.com/workspace/preview"
|
||||
},
|
||||
"urlPatterns": ["https://gmailmcp.googleapis.com/*"],
|
||||
"docsUrl": "https://developers.google.com/workspace/guides/configure-mcp-servers",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "paperclip-id-oauth",
|
||||
"label": "Connect Gmail",
|
||||
"key": "customer-read-oauth",
|
||||
"label": "Use your own Google OAuth app",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"capabilityProfile": {
|
||||
"key": "read",
|
||||
"label": "Read only",
|
||||
"description": "Search and read messages, threads, drafts, and labels."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a customer-owned Google OAuth client for read-only Gmail access.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://gmailmcp.googleapis.com/mcp/v1",
|
||||
"authorizationEndpoint": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"tokenEndpoint": "https://oauth2.googleapis.com/token",
|
||||
"metadataUrl": "https://accounts.google.com/.well-known/openid-configuration",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/gmail.readonly"
|
||||
],
|
||||
"oauthAuthorizationParams": {
|
||||
"access_type": "offline",
|
||||
"prompt": "consent"
|
||||
}
|
||||
},
|
||||
"guidanceMd": "Enable the Gmail and Gmail MCP APIs in your Google Cloud project, then register Paperclip's callback URI.",
|
||||
"consoleLinks": {
|
||||
"register": "https://console.cloud.google.com/auth/clients",
|
||||
"docs": "https://developers.google.com/workspace/guides/configure-mcp-servers"
|
||||
},
|
||||
"warnings": [
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S3"
|
||||
},
|
||||
{
|
||||
"key": "paperclip-draft",
|
||||
"label": "Connect with Paperclip",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"oauthStrategy": "paperclip_id_connector",
|
||||
"grantKinds": ["user"],
|
||||
"ownershipModes": ["customer"],
|
||||
"whenToUse": "Use Paperclip ID for a personal Gmail connection with centrally registered Google OAuth.",
|
||||
"connectorProfile": "gmail.draft",
|
||||
"capabilityProfile": {
|
||||
"key": "draft",
|
||||
"label": "Read & create drafts",
|
||||
"description": "Read Gmail and create drafts for review in Gmail."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"platform_shared"
|
||||
],
|
||||
"whenToUse": "Use Paperclip-managed Google OAuth to read Gmail and create drafts.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://gmailmcp.googleapis.com/mcp/v1",
|
||||
"scopesHint": [
|
||||
|
|
@ -28,11 +94,54 @@
|
|||
"https://www.googleapis.com/auth/gmail.compose"
|
||||
]
|
||||
},
|
||||
"guidanceMd": "Connect your Gmail identity. Paperclip can search and read mail and create drafts. Sending mail is not enabled.",
|
||||
"guidanceMd": "Connect Gmail to search and read mail and create drafts. Sending mail is permanently disabled.",
|
||||
"warnings": [
|
||||
"This connection is personal. Agents need an explicit install, profile, and delegation before they can use it."
|
||||
"Draft creation requires approval. Sending mail is not enabled.",
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S3"
|
||||
"riskTier": "S4"
|
||||
},
|
||||
{
|
||||
"key": "customer-draft-oauth",
|
||||
"label": "Use your own Google OAuth app",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"capabilityProfile": {
|
||||
"key": "draft",
|
||||
"label": "Read & create drafts",
|
||||
"description": "Read Gmail and create drafts for review in Gmail."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a customer-owned Google OAuth client to read Gmail and create drafts.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://gmailmcp.googleapis.com/mcp/v1",
|
||||
"authorizationEndpoint": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"tokenEndpoint": "https://oauth2.googleapis.com/token",
|
||||
"metadataUrl": "https://accounts.google.com/.well-known/openid-configuration",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/gmail.readonly",
|
||||
"https://www.googleapis.com/auth/gmail.compose"
|
||||
],
|
||||
"oauthAuthorizationParams": {
|
||||
"access_type": "offline",
|
||||
"prompt": "consent"
|
||||
}
|
||||
},
|
||||
"guidanceMd": "Enable the Gmail and Gmail MCP APIs in your Google Cloud project, then register Paperclip's callback URI.",
|
||||
"consoleLinks": {
|
||||
"register": "https://console.cloud.google.com/auth/clients",
|
||||
"docs": "https://developers.google.com/workspace/guides/configure-mcp-servers"
|
||||
},
|
||||
"warnings": [
|
||||
"Draft creation requires approval. Sending mail is not enabled.",
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S4"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,184 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "google-calendar",
|
||||
"name": "Google Calendar",
|
||||
"description": "Read calendars and manage Google Calendar events.",
|
||||
"categories": [
|
||||
"productivity"
|
||||
],
|
||||
"featured": true,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/google-calendar.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://calendarmcp.googleapis.com/*",
|
||||
"https://calendar.google.com/*"
|
||||
],
|
||||
"docsUrl": "https://developers.google.com/workspace/calendar/api/reference/mcp",
|
||||
"setupPrerequisite": {
|
||||
"title": "Google Developer Preview access required",
|
||||
"description": "Google must register both the Workspace email used to authorize Paperclip and the Google Cloud project that owns the OAuth client. Registration is limited to those emails and projects; it does not enable unrelated Paperclip customers.",
|
||||
"steps": [
|
||||
"Apply with the Workspace email that will sign in and the Google Cloud project that owns the OAuth client.",
|
||||
"Wait for the Google Group notification and then Google's final project-registration email, usually within a couple of days.",
|
||||
"Add every additional tester email or Cloud project through Google's member request forms before connecting."
|
||||
],
|
||||
"actionLabel": "Apply or verify Developer Preview enrollment",
|
||||
"actionUrl": "https://developers.google.com/workspace/preview"
|
||||
},
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "paperclip-read",
|
||||
"label": "Connect with Paperclip",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"oauthStrategy": "paperclip_id_connector",
|
||||
"connectorProfile": "calendar.read",
|
||||
"capabilityProfile": {
|
||||
"key": "read",
|
||||
"label": "Read only",
|
||||
"description": "Read calendars, events, and availability."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"platform_shared"
|
||||
],
|
||||
"whenToUse": "Use Paperclip-managed OAuth for read-only Calendar access.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://calendarmcp.googleapis.com/mcp/v1",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/calendar.calendarlist.readonly",
|
||||
"https://www.googleapis.com/auth/calendar.events.freebusy",
|
||||
"https://www.googleapis.com/auth/calendar.events.readonly"
|
||||
]
|
||||
},
|
||||
"guidanceMd": "Connect Google Calendar to read schedules and availability.",
|
||||
"warnings": [
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S3"
|
||||
},
|
||||
{
|
||||
"key": "customer-read-oauth",
|
||||
"label": "Use your own Google OAuth app",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"capabilityProfile": {
|
||||
"key": "read",
|
||||
"label": "Read only",
|
||||
"description": "Read calendars, events, and availability."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a customer-owned OAuth client for read-only Calendar access.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://calendarmcp.googleapis.com/mcp/v1",
|
||||
"authorizationEndpoint": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"tokenEndpoint": "https://oauth2.googleapis.com/token",
|
||||
"metadataUrl": "https://accounts.google.com/.well-known/openid-configuration",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/calendar.calendarlist.readonly",
|
||||
"https://www.googleapis.com/auth/calendar.events.freebusy",
|
||||
"https://www.googleapis.com/auth/calendar.events.readonly"
|
||||
],
|
||||
"oauthAuthorizationParams": {
|
||||
"access_type": "offline",
|
||||
"prompt": "consent"
|
||||
}
|
||||
},
|
||||
"guidanceMd": "Enable the Calendar and Calendar MCP APIs, then register Paperclip's callback URI.",
|
||||
"consoleLinks": {
|
||||
"register": "https://console.cloud.google.com/auth/clients",
|
||||
"docs": "https://developers.google.com/workspace/guides/configure-mcp-servers"
|
||||
},
|
||||
"warnings": [
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S3"
|
||||
},
|
||||
{
|
||||
"key": "paperclip-write",
|
||||
"label": "Connect with Paperclip",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"oauthStrategy": "paperclip_id_connector",
|
||||
"connectorProfile": "calendar.write",
|
||||
"capabilityProfile": {
|
||||
"key": "write",
|
||||
"label": "Read & manage",
|
||||
"description": "Create, update, respond to, and delete events."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"platform_shared"
|
||||
],
|
||||
"whenToUse": "Use Paperclip-managed OAuth to read and manage Calendar events.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://calendarmcp.googleapis.com/mcp/v1",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/calendar.calendarlist.readonly",
|
||||
"https://www.googleapis.com/auth/calendar.events.freebusy",
|
||||
"https://www.googleapis.com/auth/calendar.events"
|
||||
]
|
||||
},
|
||||
"guidanceMd": "Connect Google Calendar to read and manage events.",
|
||||
"warnings": [
|
||||
"All event mutations require approval.",
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S4"
|
||||
},
|
||||
{
|
||||
"key": "customer-write-oauth",
|
||||
"label": "Use your own Google OAuth app",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"capabilityProfile": {
|
||||
"key": "write",
|
||||
"label": "Read & manage",
|
||||
"description": "Create, update, respond to, and delete events."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a customer-owned OAuth client to read and manage Calendar events.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://calendarmcp.googleapis.com/mcp/v1",
|
||||
"authorizationEndpoint": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"tokenEndpoint": "https://oauth2.googleapis.com/token",
|
||||
"metadataUrl": "https://accounts.google.com/.well-known/openid-configuration",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/calendar.calendarlist.readonly",
|
||||
"https://www.googleapis.com/auth/calendar.events.freebusy",
|
||||
"https://www.googleapis.com/auth/calendar.events"
|
||||
],
|
||||
"oauthAuthorizationParams": {
|
||||
"access_type": "offline",
|
||||
"prompt": "consent"
|
||||
}
|
||||
},
|
||||
"guidanceMd": "Enable the Calendar and Calendar MCP APIs, then register Paperclip's callback URI.",
|
||||
"consoleLinks": {
|
||||
"register": "https://console.cloud.google.com/auth/clients",
|
||||
"docs": "https://developers.google.com/workspace/guides/configure-mcp-servers"
|
||||
},
|
||||
"warnings": [
|
||||
"All event mutations require approval.",
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S4"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "google-chat",
|
||||
"name": "Google Chat",
|
||||
"description": "Search and read Google Chat conversations and send messages.",
|
||||
"categories": [
|
||||
"communication",
|
||||
"productivity"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/google-chat.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://chatmcp.googleapis.com/*",
|
||||
"https://chat.google.com/*"
|
||||
],
|
||||
"docsUrl": "https://developers.google.com/workspace/chat/api/reference/mcp",
|
||||
"setupPrerequisite": {
|
||||
"title": "Google Developer Preview access required",
|
||||
"description": "Google must register both the Workspace email used to authorize Paperclip and the Google Cloud project that owns the OAuth client. Registration is limited to those emails and projects; it does not enable unrelated Paperclip customers.",
|
||||
"steps": [
|
||||
"Apply with the Workspace email that will sign in and the Google Cloud project that owns the OAuth client.",
|
||||
"Wait for the Google Group notification and then Google's final project-registration email, usually within a couple of days.",
|
||||
"Add every additional tester email or Cloud project through Google's member request forms before connecting."
|
||||
],
|
||||
"actionLabel": "Apply or verify Developer Preview enrollment",
|
||||
"actionUrl": "https://developers.google.com/workspace/preview"
|
||||
},
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "paperclip-read",
|
||||
"label": "Connect with Paperclip",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"oauthStrategy": "paperclip_id_connector",
|
||||
"connectorProfile": "chat.read",
|
||||
"capabilityProfile": {
|
||||
"key": "read",
|
||||
"label": "Read only",
|
||||
"description": "Search conversations and read messages."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"platform_shared"
|
||||
],
|
||||
"whenToUse": "Use Paperclip-managed OAuth for read-only Chat access.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://chatmcp.googleapis.com/mcp/v1",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/chat.spaces.readonly",
|
||||
"https://www.googleapis.com/auth/chat.memberships.readonly",
|
||||
"https://www.googleapis.com/auth/chat.messages.readonly",
|
||||
"https://www.googleapis.com/auth/chat.users.readstate.readonly"
|
||||
]
|
||||
},
|
||||
"guidanceMd": "Connect Google Chat to search conversations and read messages.",
|
||||
"warnings": [
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S3"
|
||||
},
|
||||
{
|
||||
"key": "customer-read-oauth",
|
||||
"label": "Use your own Google OAuth app",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"capabilityProfile": {
|
||||
"key": "read",
|
||||
"label": "Read only",
|
||||
"description": "Search conversations and read messages."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a customer-owned OAuth client for read-only Chat access.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://chatmcp.googleapis.com/mcp/v1",
|
||||
"authorizationEndpoint": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"tokenEndpoint": "https://oauth2.googleapis.com/token",
|
||||
"metadataUrl": "https://accounts.google.com/.well-known/openid-configuration",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/chat.spaces.readonly",
|
||||
"https://www.googleapis.com/auth/chat.memberships.readonly",
|
||||
"https://www.googleapis.com/auth/chat.messages.readonly",
|
||||
"https://www.googleapis.com/auth/chat.users.readstate.readonly"
|
||||
],
|
||||
"oauthAuthorizationParams": {
|
||||
"access_type": "offline",
|
||||
"prompt": "consent"
|
||||
}
|
||||
},
|
||||
"guidanceMd": "Enable the Chat and Chat MCP APIs, configure a Chat app, then register Paperclip's callback URI.",
|
||||
"consoleLinks": {
|
||||
"register": "https://console.cloud.google.com/auth/clients",
|
||||
"docs": "https://developers.google.com/workspace/guides/configure-mcp-servers"
|
||||
},
|
||||
"warnings": [
|
||||
"A Google Chat app must be configured in the Cloud project.",
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S3"
|
||||
},
|
||||
{
|
||||
"key": "paperclip-write",
|
||||
"label": "Connect with Paperclip",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"oauthStrategy": "paperclip_id_connector",
|
||||
"connectorProfile": "chat.write",
|
||||
"capabilityProfile": {
|
||||
"key": "write",
|
||||
"label": "Read & send",
|
||||
"description": "Read Chat and send messages."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"platform_shared"
|
||||
],
|
||||
"whenToUse": "Use Paperclip-managed OAuth to read Chat and send messages.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://chatmcp.googleapis.com/mcp/v1",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/chat.spaces.readonly",
|
||||
"https://www.googleapis.com/auth/chat.memberships.readonly",
|
||||
"https://www.googleapis.com/auth/chat.messages.readonly",
|
||||
"https://www.googleapis.com/auth/chat.users.readstate.readonly",
|
||||
"https://www.googleapis.com/auth/chat.messages.create"
|
||||
]
|
||||
},
|
||||
"guidanceMd": "Connect Google Chat to read conversations and send approved messages.",
|
||||
"warnings": [
|
||||
"Sending messages requires approval.",
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S4"
|
||||
},
|
||||
{
|
||||
"key": "customer-write-oauth",
|
||||
"label": "Use your own Google OAuth app",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"capabilityProfile": {
|
||||
"key": "write",
|
||||
"label": "Read & send",
|
||||
"description": "Read Chat and send messages."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a customer-owned OAuth client to read Chat and send messages.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://chatmcp.googleapis.com/mcp/v1",
|
||||
"authorizationEndpoint": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"tokenEndpoint": "https://oauth2.googleapis.com/token",
|
||||
"metadataUrl": "https://accounts.google.com/.well-known/openid-configuration",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/chat.spaces.readonly",
|
||||
"https://www.googleapis.com/auth/chat.memberships.readonly",
|
||||
"https://www.googleapis.com/auth/chat.messages.readonly",
|
||||
"https://www.googleapis.com/auth/chat.users.readstate.readonly",
|
||||
"https://www.googleapis.com/auth/chat.messages.create"
|
||||
],
|
||||
"oauthAuthorizationParams": {
|
||||
"access_type": "offline",
|
||||
"prompt": "consent"
|
||||
}
|
||||
},
|
||||
"guidanceMd": "Enable the Chat and Chat MCP APIs, configure a Chat app, then register Paperclip's callback URI.",
|
||||
"consoleLinks": {
|
||||
"register": "https://console.cloud.google.com/auth/clients",
|
||||
"docs": "https://developers.google.com/workspace/guides/configure-mcp-servers"
|
||||
},
|
||||
"warnings": [
|
||||
"Sending messages requires approval.",
|
||||
"A Google Chat app must be configured in the Cloud project.",
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S4"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "google-docs",
|
||||
"name": "Google Docs",
|
||||
"description": "Read and update Google Docs documents.",
|
||||
"categories": [
|
||||
"content",
|
||||
"productivity"
|
||||
],
|
||||
"featured": true,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/google-docs.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://docsmcp.googleapis.com/*",
|
||||
"https://docs.google.com/document/*"
|
||||
],
|
||||
"docsUrl": "https://developers.google.com/workspace/docs/api/reference/mcp",
|
||||
"setupPrerequisite": {
|
||||
"title": "Google Developer Preview access required",
|
||||
"description": "Google must register both the Workspace email used to authorize Paperclip and the Google Cloud project that owns the OAuth client. Registration is limited to those emails and projects; it does not enable unrelated Paperclip customers.",
|
||||
"steps": [
|
||||
"Apply with the Workspace email that will sign in and the Google Cloud project that owns the OAuth client.",
|
||||
"Wait for the Google Group notification and then Google's final project-registration email, usually within a couple of days.",
|
||||
"Add every additional tester email or Cloud project through Google's member request forms before connecting."
|
||||
],
|
||||
"actionLabel": "Apply or verify Developer Preview enrollment",
|
||||
"actionUrl": "https://developers.google.com/workspace/preview"
|
||||
},
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "paperclip-read",
|
||||
"label": "Connect with Paperclip",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"oauthStrategy": "paperclip_id_connector",
|
||||
"connectorProfile": "docs.read",
|
||||
"capabilityProfile": {
|
||||
"key": "read",
|
||||
"label": "Read only",
|
||||
"description": "Read document text and structure."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"platform_shared"
|
||||
],
|
||||
"whenToUse": "Use Paperclip-managed OAuth for read-only Docs access.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://docsmcp.googleapis.com/mcp/v1",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/drive.readonly",
|
||||
"https://www.googleapis.com/auth/documents.readonly"
|
||||
]
|
||||
},
|
||||
"guidanceMd": "Connect Google Docs to read documents.",
|
||||
"warnings": [
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S3"
|
||||
},
|
||||
{
|
||||
"key": "customer-read-oauth",
|
||||
"label": "Use your own Google OAuth app",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"capabilityProfile": {
|
||||
"key": "read",
|
||||
"label": "Read only",
|
||||
"description": "Read document text and structure."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a customer-owned OAuth client for read-only Docs access.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://docsmcp.googleapis.com/mcp/v1",
|
||||
"authorizationEndpoint": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"tokenEndpoint": "https://oauth2.googleapis.com/token",
|
||||
"metadataUrl": "https://accounts.google.com/.well-known/openid-configuration",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/drive.readonly",
|
||||
"https://www.googleapis.com/auth/documents.readonly"
|
||||
],
|
||||
"oauthAuthorizationParams": {
|
||||
"access_type": "offline",
|
||||
"prompt": "consent"
|
||||
}
|
||||
},
|
||||
"guidanceMd": "Enable the Drive, Docs, and Docs MCP APIs, then register Paperclip's callback URI.",
|
||||
"consoleLinks": {
|
||||
"register": "https://console.cloud.google.com/auth/clients",
|
||||
"docs": "https://developers.google.com/workspace/guides/configure-mcp-servers"
|
||||
},
|
||||
"warnings": [
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S3"
|
||||
},
|
||||
{
|
||||
"key": "paperclip-write",
|
||||
"label": "Connect with Paperclip",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"oauthStrategy": "paperclip_id_connector",
|
||||
"connectorProfile": "docs.write",
|
||||
"capabilityProfile": {
|
||||
"key": "write",
|
||||
"label": "Read & edit",
|
||||
"description": "Read and update documents."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"platform_shared"
|
||||
],
|
||||
"whenToUse": "Use Paperclip-managed OAuth to read and update Docs.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://docsmcp.googleapis.com/mcp/v1",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/drive.readonly",
|
||||
"https://www.googleapis.com/auth/drive.file",
|
||||
"https://www.googleapis.com/auth/documents"
|
||||
]
|
||||
},
|
||||
"guidanceMd": "Connect Google Docs to read and update documents.",
|
||||
"warnings": [
|
||||
"Document updates require approval.",
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S4"
|
||||
},
|
||||
{
|
||||
"key": "customer-write-oauth",
|
||||
"label": "Use your own Google OAuth app",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"capabilityProfile": {
|
||||
"key": "write",
|
||||
"label": "Read & edit",
|
||||
"description": "Read and update documents."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a customer-owned OAuth client to read and update Docs.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://docsmcp.googleapis.com/mcp/v1",
|
||||
"authorizationEndpoint": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"tokenEndpoint": "https://oauth2.googleapis.com/token",
|
||||
"metadataUrl": "https://accounts.google.com/.well-known/openid-configuration",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/drive.readonly",
|
||||
"https://www.googleapis.com/auth/drive.file",
|
||||
"https://www.googleapis.com/auth/documents"
|
||||
],
|
||||
"oauthAuthorizationParams": {
|
||||
"access_type": "offline",
|
||||
"prompt": "consent"
|
||||
}
|
||||
},
|
||||
"guidanceMd": "Enable the Drive, Docs, and Docs MCP APIs, then register Paperclip's callback URI.",
|
||||
"consoleLinks": {
|
||||
"register": "https://console.cloud.google.com/auth/clients",
|
||||
"docs": "https://developers.google.com/workspace/guides/configure-mcp-servers"
|
||||
},
|
||||
"warnings": [
|
||||
"Document updates require approval.",
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S4"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "google-drive",
|
||||
"name": "Google Drive",
|
||||
"description": "Search, read, create, and copy files in Google Drive.",
|
||||
"categories": [
|
||||
"content",
|
||||
"productivity"
|
||||
],
|
||||
"featured": true,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/google-drive.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://drivemcp.googleapis.com/*",
|
||||
"https://drive.google.com/*"
|
||||
],
|
||||
"docsUrl": "https://developers.google.com/workspace/drive/api/reference/mcp",
|
||||
"setupPrerequisite": {
|
||||
"title": "Google Developer Preview access required",
|
||||
"description": "Google must register both the Workspace email used to authorize Paperclip and the Google Cloud project that owns the OAuth client. Registration is limited to those emails and projects; it does not enable unrelated Paperclip customers.",
|
||||
"steps": [
|
||||
"Apply with the Workspace email that will sign in and the Google Cloud project that owns the OAuth client.",
|
||||
"Wait for the Google Group notification and then Google's final project-registration email, usually within a couple of days.",
|
||||
"Add every additional tester email or Cloud project through Google's member request forms before connecting."
|
||||
],
|
||||
"actionLabel": "Apply or verify Developer Preview enrollment",
|
||||
"actionUrl": "https://developers.google.com/workspace/preview"
|
||||
},
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "paperclip-read",
|
||||
"label": "Connect with Paperclip",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"oauthStrategy": "paperclip_id_connector",
|
||||
"connectorProfile": "drive.read",
|
||||
"capabilityProfile": {
|
||||
"key": "read",
|
||||
"label": "Read only",
|
||||
"description": "Search and read files and metadata."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"platform_shared"
|
||||
],
|
||||
"whenToUse": "Use Paperclip-managed OAuth for read-only Drive access.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://drivemcp.googleapis.com/mcp/v1",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/drive.readonly"
|
||||
]
|
||||
},
|
||||
"guidanceMd": "Connect Google Drive to search and read files. Google's Developer Preview Program must register the signed-in Workspace account and Google Cloud project before tools can run.",
|
||||
"warnings": [
|
||||
"Before connecting, enroll the signed-in Workspace account and Google Cloud project in Google's Developer Preview Program and wait for the registration confirmation."
|
||||
],
|
||||
"riskTier": "S3"
|
||||
},
|
||||
{
|
||||
"key": "customer-read-oauth",
|
||||
"label": "Use your own Google OAuth app",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"capabilityProfile": {
|
||||
"key": "read",
|
||||
"label": "Read only",
|
||||
"description": "Search and read files and metadata."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a customer-owned OAuth client for read-only Drive access.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://drivemcp.googleapis.com/mcp/v1",
|
||||
"authorizationEndpoint": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"tokenEndpoint": "https://oauth2.googleapis.com/token",
|
||||
"metadataUrl": "https://accounts.google.com/.well-known/openid-configuration",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/drive.readonly"
|
||||
],
|
||||
"oauthAuthorizationParams": {
|
||||
"access_type": "offline",
|
||||
"prompt": "consent"
|
||||
}
|
||||
},
|
||||
"guidanceMd": "Enroll the Workspace account and Google Cloud project in Google's Developer Preview Program, enable the Drive and Drive MCP APIs, then register Paperclip's callback URI.",
|
||||
"consoleLinks": {
|
||||
"register": "https://console.cloud.google.com/auth/clients",
|
||||
"docs": "https://developers.google.com/workspace/guides/configure-mcp-servers"
|
||||
},
|
||||
"warnings": [
|
||||
"Before connecting, enroll the signed-in Workspace account and Google Cloud project in Google's Developer Preview Program and wait for the registration confirmation."
|
||||
],
|
||||
"riskTier": "S3"
|
||||
},
|
||||
{
|
||||
"key": "paperclip-write",
|
||||
"label": "Connect with Paperclip",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"oauthStrategy": "paperclip_id_connector",
|
||||
"connectorProfile": "drive.write",
|
||||
"capabilityProfile": {
|
||||
"key": "write",
|
||||
"label": "Read & create",
|
||||
"description": "Read files and create or copy files."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"platform_shared"
|
||||
],
|
||||
"whenToUse": "Use Paperclip-managed OAuth for Drive read and create access.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://drivemcp.googleapis.com/mcp/v1",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/drive.readonly",
|
||||
"https://www.googleapis.com/auth/drive.file"
|
||||
]
|
||||
},
|
||||
"guidanceMd": "Connect Google Drive to read files and create or copy app-accessible files. Google's Developer Preview Program must register the signed-in Workspace account and Google Cloud project before tools can run.",
|
||||
"warnings": [
|
||||
"File creation and copying require approval.",
|
||||
"Before connecting, enroll the signed-in Workspace account and Google Cloud project in Google's Developer Preview Program and wait for the registration confirmation."
|
||||
],
|
||||
"riskTier": "S4"
|
||||
},
|
||||
{
|
||||
"key": "customer-write-oauth",
|
||||
"label": "Use your own Google OAuth app",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"capabilityProfile": {
|
||||
"key": "write",
|
||||
"label": "Read & create",
|
||||
"description": "Read files and create or copy files."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a customer-owned OAuth client for Drive read and create access.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://drivemcp.googleapis.com/mcp/v1",
|
||||
"authorizationEndpoint": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"tokenEndpoint": "https://oauth2.googleapis.com/token",
|
||||
"metadataUrl": "https://accounts.google.com/.well-known/openid-configuration",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/drive.readonly",
|
||||
"https://www.googleapis.com/auth/drive.file"
|
||||
],
|
||||
"oauthAuthorizationParams": {
|
||||
"access_type": "offline",
|
||||
"prompt": "consent"
|
||||
}
|
||||
},
|
||||
"guidanceMd": "Enroll the Workspace account and Google Cloud project in Google's Developer Preview Program, enable the Drive and Drive MCP APIs, then register Paperclip's callback URI.",
|
||||
"consoleLinks": {
|
||||
"register": "https://console.cloud.google.com/auth/clients",
|
||||
"docs": "https://developers.google.com/workspace/guides/configure-mcp-servers"
|
||||
},
|
||||
"warnings": [
|
||||
"File creation and copying require approval.",
|
||||
"Before connecting, enroll the signed-in Workspace account and Google Cloud project in Google's Developer Preview Program and wait for the registration confirmation."
|
||||
],
|
||||
"riskTier": "S4"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "google-people",
|
||||
"name": "Google People",
|
||||
"description": "Search contacts and directory profiles with the Google People API.",
|
||||
"categories": [
|
||||
"communication",
|
||||
"productivity"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/google-people.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://people.googleapis.com/*"
|
||||
],
|
||||
"docsUrl": "https://developers.google.com/people/api/mcp",
|
||||
"setupPrerequisite": {
|
||||
"title": "Google Developer Preview access required",
|
||||
"description": "Google must register both the Workspace email used to authorize Paperclip and the Google Cloud project that owns the OAuth client. Registration is limited to those emails and projects; it does not enable unrelated Paperclip customers.",
|
||||
"steps": [
|
||||
"Apply with the Workspace email that will sign in and the Google Cloud project that owns the OAuth client.",
|
||||
"Wait for the Google Group notification and then Google's final project-registration email, usually within a couple of days.",
|
||||
"Add every additional tester email or Cloud project through Google's member request forms before connecting."
|
||||
],
|
||||
"actionLabel": "Apply or verify Developer Preview enrollment",
|
||||
"actionUrl": "https://developers.google.com/workspace/preview"
|
||||
},
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "paperclip-read",
|
||||
"label": "Connect with Paperclip",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"oauthStrategy": "paperclip_id_connector",
|
||||
"connectorProfile": "people.read",
|
||||
"capabilityProfile": {
|
||||
"key": "read",
|
||||
"label": "Read contacts",
|
||||
"description": "Search contacts, directory people, and your profile."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"platform_shared"
|
||||
],
|
||||
"whenToUse": "Use Paperclip-managed OAuth for Google People access.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://people.googleapis.com/mcp/v1",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/directory.readonly",
|
||||
"https://www.googleapis.com/auth/userinfo.profile",
|
||||
"https://www.googleapis.com/auth/contacts.readonly"
|
||||
]
|
||||
},
|
||||
"guidanceMd": "Connect Google People to search contacts and directory profiles.",
|
||||
"warnings": [
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S3"
|
||||
},
|
||||
{
|
||||
"key": "customer-read-oauth",
|
||||
"label": "Use your own Google OAuth app",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"capabilityProfile": {
|
||||
"key": "read",
|
||||
"label": "Read contacts",
|
||||
"description": "Search contacts, directory people, and your profile."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a customer-owned OAuth client for Google People access.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://people.googleapis.com/mcp/v1",
|
||||
"authorizationEndpoint": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"tokenEndpoint": "https://oauth2.googleapis.com/token",
|
||||
"metadataUrl": "https://accounts.google.com/.well-known/openid-configuration",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/directory.readonly",
|
||||
"https://www.googleapis.com/auth/userinfo.profile",
|
||||
"https://www.googleapis.com/auth/contacts.readonly"
|
||||
],
|
||||
"oauthAuthorizationParams": {
|
||||
"access_type": "offline",
|
||||
"prompt": "consent"
|
||||
}
|
||||
},
|
||||
"guidanceMd": "Enable the People and People MCP APIs, then register Paperclip's callback URI.",
|
||||
"consoleLinks": {
|
||||
"register": "https://console.cloud.google.com/auth/clients",
|
||||
"docs": "https://developers.google.com/workspace/guides/configure-mcp-servers"
|
||||
},
|
||||
"warnings": [
|
||||
"Directory search availability depends on your Workspace account.",
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S3"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -2,27 +2,187 @@
|
|||
"schemaVersion": 1,
|
||||
"slug": "google-sheets",
|
||||
"name": "Google Sheets",
|
||||
"description": "Read and update selected spreadsheets.",
|
||||
"description": "Read and update Google Sheets spreadsheets.",
|
||||
"categories": [
|
||||
"data"
|
||||
"data",
|
||||
"productivity"
|
||||
],
|
||||
"featured": false,
|
||||
"featured": true,
|
||||
"branding": {
|
||||
"logoUrl": "https://www.google.com/s2/favicons?domain=sheets.google.com&sz=128"
|
||||
"logoUrl": "/brands/apps/google-sheets.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://sheetsmcp.googleapis.com/*",
|
||||
"https://docs.google.com/spreadsheets/*",
|
||||
"https://sheets.google.com/*"
|
||||
],
|
||||
"docsUrl": "https://developers.google.com/workspace/sheets/api/reference/mcp",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "local",
|
||||
"transport": "local_stdio",
|
||||
"auth": "none",
|
||||
"key": "paperclip-read",
|
||||
"label": "Connect with Paperclip",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"oauthStrategy": "paperclip_id_connector",
|
||||
"connectorProfile": "sheets.read",
|
||||
"capabilityProfile": {
|
||||
"key": "read",
|
||||
"label": "Read only",
|
||||
"description": "Read spreadsheet values and structure."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"platform_shared"
|
||||
],
|
||||
"whenToUse": "Use Paperclip-managed OAuth for read-only Sheets access.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://sheetsmcp.googleapis.com/mcp/v1",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/drive.readonly",
|
||||
"https://www.googleapis.com/auth/spreadsheets.readonly"
|
||||
]
|
||||
},
|
||||
"guidanceMd": "Connect Google Sheets to read spreadsheets.",
|
||||
"warnings": [
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S3"
|
||||
},
|
||||
{
|
||||
"key": "customer-read-oauth",
|
||||
"label": "Use your own Google OAuth app",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"capabilityProfile": {
|
||||
"key": "read",
|
||||
"label": "Read only",
|
||||
"description": "Read spreadsheet values and structure."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use credentials from your provider account.",
|
||||
"whenToUse": "Use a customer-owned OAuth client for read-only Sheets access.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://sheetsmcp.googleapis.com/mcp/v1",
|
||||
"authorizationEndpoint": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"tokenEndpoint": "https://oauth2.googleapis.com/token",
|
||||
"metadataUrl": "https://accounts.google.com/.well-known/openid-configuration",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/drive.readonly",
|
||||
"https://www.googleapis.com/auth/spreadsheets.readonly"
|
||||
],
|
||||
"oauthAuthorizationParams": {
|
||||
"access_type": "offline",
|
||||
"prompt": "consent"
|
||||
}
|
||||
},
|
||||
"guidanceMd": "Enable the Drive, Sheets, and Sheets MCP APIs, then register Paperclip's callback URI.",
|
||||
"consoleLinks": {
|
||||
"register": "https://console.cloud.google.com/auth/clients",
|
||||
"docs": "https://developers.google.com/workspace/guides/configure-mcp-servers"
|
||||
},
|
||||
"warnings": [
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S3"
|
||||
},
|
||||
{
|
||||
"key": "paperclip-write",
|
||||
"label": "Connect with Paperclip",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"oauthStrategy": "paperclip_id_connector",
|
||||
"connectorProfile": "sheets.write",
|
||||
"capabilityProfile": {
|
||||
"key": "write",
|
||||
"label": "Read & edit",
|
||||
"description": "Read and update spreadsheet values, formulas, and dimensions."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"platform_shared"
|
||||
],
|
||||
"whenToUse": "Use Paperclip-managed OAuth to read and update Sheets.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://sheetsmcp.googleapis.com/mcp/v1",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/drive.readonly",
|
||||
"https://www.googleapis.com/auth/drive.file",
|
||||
"https://www.googleapis.com/auth/spreadsheets"
|
||||
]
|
||||
},
|
||||
"guidanceMd": "Connect Google Sheets to read and update spreadsheets.",
|
||||
"warnings": [
|
||||
"Spreadsheet updates require approval.",
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S4"
|
||||
},
|
||||
{
|
||||
"key": "customer-write-oauth",
|
||||
"label": "Use your own Google OAuth app",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"capabilityProfile": {
|
||||
"key": "write",
|
||||
"label": "Read & edit",
|
||||
"description": "Read and update spreadsheet values, formulas, and dimensions."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a customer-owned OAuth client to read and update Sheets.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://sheetsmcp.googleapis.com/mcp/v1",
|
||||
"authorizationEndpoint": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"tokenEndpoint": "https://oauth2.googleapis.com/token",
|
||||
"metadataUrl": "https://accounts.google.com/.well-known/openid-configuration",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/drive.readonly",
|
||||
"https://www.googleapis.com/auth/drive.file",
|
||||
"https://www.googleapis.com/auth/spreadsheets"
|
||||
],
|
||||
"oauthAuthorizationParams": {
|
||||
"access_type": "offline",
|
||||
"prompt": "consent"
|
||||
}
|
||||
},
|
||||
"guidanceMd": "Enable the Drive, Sheets, and Sheets MCP APIs, then register Paperclip's callback URI.",
|
||||
"consoleLinks": {
|
||||
"register": "https://console.cloud.google.com/auth/clients",
|
||||
"docs": "https://developers.google.com/workspace/guides/configure-mcp-servers"
|
||||
},
|
||||
"warnings": [
|
||||
"Spreadsheet updates require approval.",
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S4"
|
||||
},
|
||||
{
|
||||
"key": "local",
|
||||
"label": "Use the Paperclip robot account",
|
||||
"transport": "local_stdio",
|
||||
"auth": "none",
|
||||
"capabilityProfile": {
|
||||
"key": "robot",
|
||||
"label": "Share selected sheets",
|
||||
"description": "Share only named spreadsheets with the Paperclip robot account."
|
||||
},
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Share selected spreadsheets with the Paperclip robot account instead of connecting a Google identity.",
|
||||
"defaults": {
|
||||
"templateKey": "paperclip.google-sheets"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,183 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "google-slides",
|
||||
"name": "Google Slides",
|
||||
"description": "Read and update Google Slides presentations.",
|
||||
"categories": [
|
||||
"content",
|
||||
"productivity"
|
||||
],
|
||||
"featured": true,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/google-slides.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://slidesmcp.googleapis.com/*",
|
||||
"https://docs.google.com/presentation/*"
|
||||
],
|
||||
"docsUrl": "https://developers.google.com/workspace/slides/api/reference/mcp",
|
||||
"setupPrerequisite": {
|
||||
"title": "Google Developer Preview access required",
|
||||
"description": "Google must register both the Workspace email used to authorize Paperclip and the Google Cloud project that owns the OAuth client. Registration is limited to those emails and projects; it does not enable unrelated Paperclip customers.",
|
||||
"steps": [
|
||||
"Apply with the Workspace email that will sign in and the Google Cloud project that owns the OAuth client.",
|
||||
"Wait for the Google Group notification and then Google's final project-registration email, usually within a couple of days.",
|
||||
"Add every additional tester email or Cloud project through Google's member request forms before connecting."
|
||||
],
|
||||
"actionLabel": "Apply or verify Developer Preview enrollment",
|
||||
"actionUrl": "https://developers.google.com/workspace/preview"
|
||||
},
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "paperclip-read",
|
||||
"label": "Connect with Paperclip",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"oauthStrategy": "paperclip_id_connector",
|
||||
"connectorProfile": "slides.read",
|
||||
"capabilityProfile": {
|
||||
"key": "read",
|
||||
"label": "Read only",
|
||||
"description": "Read presentation slides and content."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"platform_shared"
|
||||
],
|
||||
"whenToUse": "Use Paperclip-managed OAuth for read-only Slides access.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://slidesmcp.googleapis.com/mcp/v1",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/drive.readonly",
|
||||
"https://www.googleapis.com/auth/presentations.readonly"
|
||||
]
|
||||
},
|
||||
"guidanceMd": "Connect Google Slides to read presentations.",
|
||||
"warnings": [
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S3"
|
||||
},
|
||||
{
|
||||
"key": "customer-read-oauth",
|
||||
"label": "Use your own Google OAuth app",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"capabilityProfile": {
|
||||
"key": "read",
|
||||
"label": "Read only",
|
||||
"description": "Read presentation slides and content."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a customer-owned OAuth client for read-only Slides access.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://slidesmcp.googleapis.com/mcp/v1",
|
||||
"authorizationEndpoint": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"tokenEndpoint": "https://oauth2.googleapis.com/token",
|
||||
"metadataUrl": "https://accounts.google.com/.well-known/openid-configuration",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/drive.readonly",
|
||||
"https://www.googleapis.com/auth/presentations.readonly"
|
||||
],
|
||||
"oauthAuthorizationParams": {
|
||||
"access_type": "offline",
|
||||
"prompt": "consent"
|
||||
}
|
||||
},
|
||||
"guidanceMd": "Enable the Drive, Slides, and Slides MCP APIs, then register Paperclip's callback URI.",
|
||||
"consoleLinks": {
|
||||
"register": "https://console.cloud.google.com/auth/clients",
|
||||
"docs": "https://developers.google.com/workspace/guides/configure-mcp-servers"
|
||||
},
|
||||
"warnings": [
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S3"
|
||||
},
|
||||
{
|
||||
"key": "paperclip-write",
|
||||
"label": "Connect with Paperclip",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"oauthStrategy": "paperclip_id_connector",
|
||||
"connectorProfile": "slides.write",
|
||||
"capabilityProfile": {
|
||||
"key": "write",
|
||||
"label": "Read & edit",
|
||||
"description": "Read and update presentations."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"platform_shared"
|
||||
],
|
||||
"whenToUse": "Use Paperclip-managed OAuth to read and update Slides.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://slidesmcp.googleapis.com/mcp/v1",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/drive.readonly",
|
||||
"https://www.googleapis.com/auth/drive.file",
|
||||
"https://www.googleapis.com/auth/presentations"
|
||||
]
|
||||
},
|
||||
"guidanceMd": "Connect Google Slides to read and update presentations.",
|
||||
"warnings": [
|
||||
"Presentation updates require approval.",
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S4"
|
||||
},
|
||||
{
|
||||
"key": "customer-write-oauth",
|
||||
"label": "Use your own Google OAuth app",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"capabilityProfile": {
|
||||
"key": "write",
|
||||
"label": "Read & edit",
|
||||
"description": "Read and update presentations."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a customer-owned OAuth client to read and update Slides.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://slidesmcp.googleapis.com/mcp/v1",
|
||||
"authorizationEndpoint": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"tokenEndpoint": "https://oauth2.googleapis.com/token",
|
||||
"metadataUrl": "https://accounts.google.com/.well-known/openid-configuration",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/drive.readonly",
|
||||
"https://www.googleapis.com/auth/drive.file",
|
||||
"https://www.googleapis.com/auth/presentations"
|
||||
],
|
||||
"oauthAuthorizationParams": {
|
||||
"access_type": "offline",
|
||||
"prompt": "consent"
|
||||
}
|
||||
},
|
||||
"guidanceMd": "Enable the Drive, Slides, and Slides MCP APIs, then register Paperclip's callback URI.",
|
||||
"consoleLinks": {
|
||||
"register": "https://console.cloud.google.com/auth/clients",
|
||||
"docs": "https://developers.google.com/workspace/guides/configure-mcp-servers"
|
||||
},
|
||||
"warnings": [
|
||||
"Presentation updates require approval.",
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S4"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "google-workspace-search",
|
||||
"name": "Google Workspace Search",
|
||||
"description": "Search Gmail, Drive, Calendar, and Chat through one read-only Google Workspace search tool.",
|
||||
"categories": [
|
||||
"data",
|
||||
"productivity"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/google-workspace-search.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://workspacemcp.googleapis.com/*"
|
||||
],
|
||||
"docsUrl": "https://developers.google.com/workspace/guides/universal-search-mcp",
|
||||
"setupPrerequisite": {
|
||||
"title": "Google Developer Preview access required",
|
||||
"description": "Google must register both the Workspace email used to authorize Paperclip and the Google Cloud project that owns the OAuth client. Registration is limited to those emails and projects; it does not enable unrelated Paperclip customers.",
|
||||
"steps": [
|
||||
"Apply with the Workspace email that will sign in and the Google Cloud project that owns the OAuth client.",
|
||||
"Wait for the Google Group notification and then Google's final project-registration email, usually within a couple of days.",
|
||||
"Add every additional tester email or Cloud project through Google's member request forms before connecting."
|
||||
],
|
||||
"actionLabel": "Apply or verify Developer Preview enrollment",
|
||||
"actionUrl": "https://developers.google.com/workspace/preview"
|
||||
},
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "paperclip-read",
|
||||
"label": "Connect with Paperclip",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"oauthStrategy": "paperclip_id_connector",
|
||||
"connectorProfile": "workspace-search.read",
|
||||
"capabilityProfile": {
|
||||
"key": "read",
|
||||
"label": "Search Workspace",
|
||||
"description": "Search Gmail, Drive, Calendar, and Chat without write access."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"platform_shared"
|
||||
],
|
||||
"whenToUse": "Use Paperclip-managed OAuth for cross-product Workspace search.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://workspacemcp.googleapis.com/mcp/v1",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/gmail.readonly",
|
||||
"https://www.googleapis.com/auth/drive.readonly",
|
||||
"https://www.googleapis.com/auth/calendar.readonly",
|
||||
"https://www.googleapis.com/auth/chat.messages.readonly"
|
||||
]
|
||||
},
|
||||
"guidanceMd": "Connect Google Workspace Search for one read-only search tool spanning Gmail, Drive, Calendar, and Chat.",
|
||||
"warnings": [
|
||||
"This requests read access to all four supported search corpora.",
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S3"
|
||||
},
|
||||
{
|
||||
"key": "customer-read-oauth",
|
||||
"label": "Use your own Google OAuth app",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"capabilityProfile": {
|
||||
"key": "read",
|
||||
"label": "Search Workspace",
|
||||
"description": "Search Gmail, Drive, Calendar, and Chat without write access."
|
||||
},
|
||||
"grantKinds": [
|
||||
"user"
|
||||
],
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a customer-owned OAuth client for cross-product Workspace search.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://workspacemcp.googleapis.com/mcp/v1",
|
||||
"authorizationEndpoint": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"tokenEndpoint": "https://oauth2.googleapis.com/token",
|
||||
"metadataUrl": "https://accounts.google.com/.well-known/openid-configuration",
|
||||
"scopesHint": [
|
||||
"https://www.googleapis.com/auth/gmail.readonly",
|
||||
"https://www.googleapis.com/auth/drive.readonly",
|
||||
"https://www.googleapis.com/auth/calendar.readonly",
|
||||
"https://www.googleapis.com/auth/chat.messages.readonly"
|
||||
],
|
||||
"oauthAuthorizationParams": {
|
||||
"access_type": "offline",
|
||||
"prompt": "consent"
|
||||
}
|
||||
},
|
||||
"guidanceMd": "Enable the Gmail, Drive, Calendar, Chat, and Workspace MCP APIs, then register Paperclip's callback URI.",
|
||||
"consoleLinks": {
|
||||
"register": "https://console.cloud.google.com/auth/clients",
|
||||
"docs": "https://developers.google.com/workspace/guides/universal-search-mcp"
|
||||
},
|
||||
"warnings": [
|
||||
"This requests read access to all four supported search corpora.",
|
||||
"Google Workspace MCP servers are in Developer Preview."
|
||||
],
|
||||
"riskTier": "S3"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "hugging-face",
|
||||
"name": "Hugging Face",
|
||||
"description": "Connect Hugging Face's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"ai"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/hugging-face.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://huggingface.co/*"
|
||||
],
|
||||
"docsUrl": "https://huggingface.co/docs/hub/agents-mcp",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://huggingface.co/mcp?login&gradio=none",
|
||||
"scopesHint": [
|
||||
"read-mcp"
|
||||
]
|
||||
},
|
||||
"guidanceMd": "Connect Hugging Face in the browser. A Hugging Face account.",
|
||||
"riskTier": "S2",
|
||||
"label": "Sign in with Hugging Face",
|
||||
"consoleLinks": {
|
||||
"docs": "https://huggingface.co/docs/hub/agents-mcp"
|
||||
},
|
||||
"warnings": [
|
||||
"A Hugging Face account."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "jira",
|
||||
"name": "Jira",
|
||||
"description": "Connect Jira's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"productivity"
|
||||
],
|
||||
"featured": true,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/jira.svg",
|
||||
"darkLogoUrl": "/brands/apps/jira-dark.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.atlassian.com/*"
|
||||
],
|
||||
"docsUrl": "https://support.atlassian.com/atlassian-rovo-mcp-server/docs/getting-started-with-the-atlassian-remote-mcp-server/",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.atlassian.com/v1/mcp/authv2",
|
||||
"scopesHint": [
|
||||
"read:me",
|
||||
"read:account",
|
||||
"offline_access",
|
||||
"email",
|
||||
"read:jira-work",
|
||||
"write:jira-work",
|
||||
"search:confluence",
|
||||
"read:confluence-user",
|
||||
"read:page:confluence",
|
||||
"write:page:confluence",
|
||||
"read:comment:confluence",
|
||||
"write:comment:confluence",
|
||||
"read:space:confluence",
|
||||
"read:hierarchical-content:confluence",
|
||||
"write:component:compass",
|
||||
"read:component:compass",
|
||||
"read:scorecard:compass",
|
||||
"write:scorecard:compass",
|
||||
"read:event:compass",
|
||||
"read:metric:compass",
|
||||
"read:all:twg",
|
||||
"write:all:twg"
|
||||
]
|
||||
},
|
||||
"guidanceMd": "Connect Jira in the browser. An active Jira or Confluence site; tenant policy may require an administrator to approve the client.",
|
||||
"riskTier": "S3",
|
||||
"label": "Sign in with Jira",
|
||||
"consoleLinks": {
|
||||
"docs": "https://support.atlassian.com/atlassian-rovo-mcp-server/docs/getting-started-with-the-atlassian-remote-mcp-server/"
|
||||
},
|
||||
"warnings": [
|
||||
"An active Jira or Confluence site; tenant policy may require an administrator to approve the client."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "kernel",
|
||||
"name": "Kernel",
|
||||
"description": "Connect Kernel's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"developer"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/kernel.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.onkernel.com/*"
|
||||
],
|
||||
"docsUrl": "https://www.kernel.sh/docs/reference/mcp-server/",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.onkernel.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Kernel in the browser. A Kernel account with either browser authorization or an API key.",
|
||||
"riskTier": "S3",
|
||||
"label": "Sign in with Kernel",
|
||||
"consoleLinks": {
|
||||
"docs": "https://www.kernel.sh/docs/reference/mcp-server/"
|
||||
},
|
||||
"warnings": [
|
||||
"A Kernel account with either browser authorization or an API key."
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "mcp-api-key",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "api_key",
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a restricted customer-owned key when browser sign-in is not suitable.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.onkernel.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Use a customer-created Kernel key. A Kernel account with either browser authorization or an API key.",
|
||||
"riskTier": "S3",
|
||||
"label": "Use an API key",
|
||||
"credentialFields": [
|
||||
{
|
||||
"key": "authorization",
|
||||
"label": "Kernel API key",
|
||||
"type": "password",
|
||||
"required": true,
|
||||
"placeholder": "Paste your Kernel API key",
|
||||
"secret": true
|
||||
}
|
||||
],
|
||||
"keyPlacement": {
|
||||
"location": "header",
|
||||
"name": "X-API-Key",
|
||||
"prefix": null
|
||||
},
|
||||
"consoleLinks": {
|
||||
"keys": "https://www.kernel.sh/docs/reference/mcp-server/",
|
||||
"docs": "https://www.kernel.sh/docs/reference/mcp-server/"
|
||||
},
|
||||
"warnings": [
|
||||
"A Kernel account with either browser authorization or an API key."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
],
|
||||
"featured": true,
|
||||
"branding": {
|
||||
"logoUrl": "https://www.google.com/s2/favicons?domain=linear.app&sz=128"
|
||||
"logoUrl": "/brands/apps/linear.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.linear.app/*"
|
||||
|
|
@ -19,8 +19,7 @@
|
|||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"customer",
|
||||
"dcr"
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use the provider-hosted connection for the quickest setup.",
|
||||
"defaults": {
|
||||
|
|
@ -38,7 +37,25 @@
|
|||
"workspace",
|
||||
"team",
|
||||
"project"
|
||||
]
|
||||
],
|
||||
"credentialSources": {
|
||||
"vercelConnect": {
|
||||
"services": [
|
||||
"linear"
|
||||
],
|
||||
"principalModes": [
|
||||
"user"
|
||||
],
|
||||
"scopes": [
|
||||
"read",
|
||||
"write"
|
||||
],
|
||||
"header": {
|
||||
"name": "Authorization",
|
||||
"prefix": "Bearer "
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "local-falcon",
|
||||
"name": "Local Falcon",
|
||||
"description": "Connect Local Falcon's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"analytics"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/local-falcon.png"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.localfalcon.com/*"
|
||||
],
|
||||
"docsUrl": "https://docs.localfalcon.com/",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.localfalcon.com"
|
||||
},
|
||||
"guidanceMd": "Connect Local Falcon in the browser. A Local Falcon account with MCP access.",
|
||||
"riskTier": "S2",
|
||||
"label": "Sign in with Local Falcon",
|
||||
"consoleLinks": {
|
||||
"docs": "https://docs.localfalcon.com/"
|
||||
},
|
||||
"warnings": [
|
||||
"A Local Falcon account with MCP access."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "make",
|
||||
"name": "Make",
|
||||
"description": "Connect Make's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"productivity"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/make.svg",
|
||||
"darkLogoUrl": "/brands/apps/make-dark.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.make.com/*"
|
||||
],
|
||||
"docsUrl": "https://developers.make.com/mcp-server",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.make.com"
|
||||
},
|
||||
"guidanceMd": "Connect Make in the browser. A Make account and access to the scenarios exposed to MCP.",
|
||||
"riskTier": "S3",
|
||||
"label": "Sign in with Make",
|
||||
"consoleLinks": {
|
||||
"docs": "https://developers.make.com/mcp-server"
|
||||
},
|
||||
"warnings": [
|
||||
"A Make account and access to the scenarios exposed to MCP."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "manufact",
|
||||
"name": "Manufact",
|
||||
"description": "Connect Manufact's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"productivity"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/manufact.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.manufact.com/*"
|
||||
],
|
||||
"docsUrl": "https://docs.manufact.com/mcp",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.manufact.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Manufact in the browser. A Manufact account with MCP access.",
|
||||
"riskTier": "S3",
|
||||
"label": "Sign in with Manufact",
|
||||
"consoleLinks": {
|
||||
"docs": "https://docs.manufact.com/mcp"
|
||||
},
|
||||
"warnings": [
|
||||
"A Manufact account with MCP access."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "mem0",
|
||||
"name": "Mem0",
|
||||
"description": "Connect Mem0's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"ai"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/mem0.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.mem0.ai/*"
|
||||
],
|
||||
"docsUrl": "https://docs.mem0.ai/platform/mem0-mcp",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-api-key",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "api_key",
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a restricted customer-owned key when browser sign-in is not suitable.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.mem0.ai/mcp/"
|
||||
},
|
||||
"guidanceMd": "Use a customer-created Mem0 key. A Mem0 API key; the live server currently requires the slash-normalized endpoint.",
|
||||
"riskTier": "S3",
|
||||
"label": "Use an API key",
|
||||
"credentialFields": [
|
||||
{
|
||||
"key": "authorization",
|
||||
"label": "Mem0 API key",
|
||||
"type": "password",
|
||||
"required": true,
|
||||
"placeholder": "m0sk_...",
|
||||
"secret": true
|
||||
}
|
||||
],
|
||||
"keyPlacement": {
|
||||
"location": "header",
|
||||
"name": "Authorization",
|
||||
"prefix": "Bearer "
|
||||
},
|
||||
"consoleLinks": {
|
||||
"keys": "https://docs.mem0.ai/platform/mem0-mcp",
|
||||
"docs": "https://docs.mem0.ai/platform/mem0-mcp"
|
||||
},
|
||||
"warnings": [
|
||||
"A Mem0 API key; the live server currently requires the slash-normalized endpoint."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "miro",
|
||||
"name": "Miro",
|
||||
"description": "Connect Miro's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"productivity"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/miro.svg",
|
||||
"darkLogoUrl": "/brands/apps/miro-dark.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.miro.com/*"
|
||||
],
|
||||
"docsUrl": "https://help.miro.com/hc/en-us/articles/31625301583890-How-to-enable-Miro-s-MCP-Server-user-guide",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.miro.com/"
|
||||
},
|
||||
"guidanceMd": "Connect Miro in the browser. A Miro account; enterprise administrators may restrict third-party MCP clients.",
|
||||
"riskTier": "S3",
|
||||
"label": "Sign in with Miro",
|
||||
"consoleLinks": {
|
||||
"docs": "https://help.miro.com/hc/en-us/articles/31625301583890-How-to-enable-Miro-s-MCP-Server-user-guide"
|
||||
},
|
||||
"warnings": [
|
||||
"A Miro account; enterprise administrators may restrict third-party MCP clients."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "mixpanel",
|
||||
"name": "Mixpanel",
|
||||
"description": "Connect Mixpanel's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"analytics"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/mixpanel.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.mixpanel.com/*"
|
||||
],
|
||||
"docsUrl": "https://mixpanel.com/blog/mixpanel-mcp-server/",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.mixpanel.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Mixpanel in the browser. A Mixpanel account; the hosted MCP server is currently beta.",
|
||||
"riskTier": "S3",
|
||||
"label": "Sign in with Mixpanel",
|
||||
"consoleLinks": {
|
||||
"docs": "https://mixpanel.com/blog/mixpanel-mcp-server/"
|
||||
},
|
||||
"warnings": [
|
||||
"A Mixpanel account; the hosted MCP server is currently beta.",
|
||||
"This provider's hosted MCP server is currently beta or preview."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "netlify",
|
||||
"name": "Netlify",
|
||||
"description": "Connect Netlify's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"developer"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/netlify.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://netlify-mcp.netlify.app/*"
|
||||
],
|
||||
"docsUrl": "https://docs.netlify.com/build/build-with-ai/agent-setup-guides/agent-setup-overview/",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://netlify-mcp.netlify.app/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Netlify in the browser. A Netlify account with access to the relevant team and sites.",
|
||||
"riskTier": "S3",
|
||||
"label": "Sign in with Netlify",
|
||||
"consoleLinks": {
|
||||
"docs": "https://docs.netlify.com/build/build-with-ai/agent-setup-guides/agent-setup-overview/"
|
||||
},
|
||||
"warnings": [
|
||||
"A Netlify account with access to the relevant team and sites."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -8,7 +8,8 @@
|
|||
],
|
||||
"featured": true,
|
||||
"branding": {
|
||||
"logoUrl": "https://www.google.com/s2/favicons?domain=notion.so&sz=128"
|
||||
"logoUrl": "/brands/apps/notion.svg",
|
||||
"darkLogoUrl": "/brands/apps/notion-dark.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.notion.com/*"
|
||||
|
|
@ -32,8 +33,26 @@
|
|||
"workspace",
|
||||
"page",
|
||||
"database"
|
||||
]
|
||||
],
|
||||
"credentialSources": {
|
||||
"vercelConnect": {
|
||||
"services": [
|
||||
"notion"
|
||||
],
|
||||
"principalModes": [
|
||||
"user"
|
||||
],
|
||||
"scopes": [
|
||||
"*"
|
||||
],
|
||||
"header": {
|
||||
"name": "Authorization",
|
||||
"prefix": "Bearer "
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"redirectConstraints": "https-or-loopback-http"
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"docsUrl": "https://developers.notion.com/guides/mcp/build-mcp-client"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "https://www.google.com/s2/favicons?domain=oauth.net&sz=128"
|
||||
"logoUrl": "/brands/apps/oauth-generic.svg"
|
||||
},
|
||||
"urlPatterns": [],
|
||||
"methods": [
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "oreilly",
|
||||
"name": "O'Reilly",
|
||||
"description": "Connect O'Reilly's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"content"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/oreilly.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://api.oreilly.com/*"
|
||||
],
|
||||
"docsUrl": "https://learning.oreilly.com/apidocs/mcp/content/",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://api.oreilly.com/api/content-discovery/v1/mcp/"
|
||||
},
|
||||
"guidanceMd": "Connect O'Reilly in the browser. An O'Reilly Learning subscription with MCP or API access.",
|
||||
"riskTier": "S2",
|
||||
"label": "Sign in with O'Reilly",
|
||||
"consoleLinks": {
|
||||
"docs": "https://learning.oreilly.com/apidocs/mcp/content/"
|
||||
},
|
||||
"warnings": [
|
||||
"An O'Reilly Learning subscription with MCP or API access."
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "mcp-api-key",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "api_key",
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a restricted customer-owned key when browser sign-in is not suitable.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://api.oreilly.com/api/content-discovery/v1/mcp/"
|
||||
},
|
||||
"guidanceMd": "Use a customer-created O'Reilly key. An O'Reilly Learning subscription with MCP or API access.",
|
||||
"riskTier": "S2",
|
||||
"label": "Use an API key",
|
||||
"credentialFields": [
|
||||
{
|
||||
"key": "authorization",
|
||||
"label": "O'Reilly API key",
|
||||
"type": "password",
|
||||
"required": true,
|
||||
"placeholder": "Paste your O'Reilly API token",
|
||||
"secret": true
|
||||
}
|
||||
],
|
||||
"keyPlacement": {
|
||||
"location": "header",
|
||||
"name": "Authorization",
|
||||
"prefix": "Bearer "
|
||||
},
|
||||
"consoleLinks": {
|
||||
"keys": "https://learning.oreilly.com/apidocs/mcp/content/",
|
||||
"docs": "https://learning.oreilly.com/apidocs/mcp/content/"
|
||||
},
|
||||
"warnings": [
|
||||
"An O'Reilly Learning subscription with MCP or API access."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "pagerduty",
|
||||
"name": "PagerDuty",
|
||||
"description": "Connect PagerDuty's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"developer"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/pagerduty.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.pagerduty.com/*"
|
||||
],
|
||||
"docsUrl": "https://support.pagerduty.com/main/docs/pagerduty-mcp-server",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-api-key-us",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "api_key",
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a restricted customer-owned key when browser sign-in is not suitable.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.pagerduty.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Use a customer-created PagerDuty key. A PagerDuty API token; choose the regional endpoint that hosts the account.",
|
||||
"riskTier": "S4",
|
||||
"label": "US service region",
|
||||
"credentialFields": [
|
||||
{
|
||||
"key": "authorization",
|
||||
"label": "PagerDuty API key",
|
||||
"type": "password",
|
||||
"required": true,
|
||||
"placeholder": "Paste your PagerDuty user API token",
|
||||
"secret": true
|
||||
}
|
||||
],
|
||||
"keyPlacement": {
|
||||
"location": "header",
|
||||
"name": "Authorization",
|
||||
"prefix": "Token token="
|
||||
},
|
||||
"consoleLinks": {
|
||||
"keys": "https://support.pagerduty.com/main/docs/pagerduty-mcp-server",
|
||||
"docs": "https://support.pagerduty.com/main/docs/pagerduty-mcp-server"
|
||||
},
|
||||
"warnings": [
|
||||
"A PagerDuty API token; choose the regional endpoint that hosts the account."
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "mcp-api-key-eu",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "api_key",
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a restricted customer-owned key when browser sign-in is not suitable.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.eu.pagerduty.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Use a customer-created PagerDuty key. A PagerDuty API token; choose the regional endpoint that hosts the account.",
|
||||
"riskTier": "S4",
|
||||
"label": "EU service region",
|
||||
"credentialFields": [
|
||||
{
|
||||
"key": "authorization",
|
||||
"label": "PagerDuty API key",
|
||||
"type": "password",
|
||||
"required": true,
|
||||
"placeholder": "Paste your PagerDuty user API token",
|
||||
"secret": true
|
||||
}
|
||||
],
|
||||
"keyPlacement": {
|
||||
"location": "header",
|
||||
"name": "Authorization",
|
||||
"prefix": "Token token="
|
||||
},
|
||||
"consoleLinks": {
|
||||
"keys": "https://support.pagerduty.com/main/docs/pagerduty-mcp-server",
|
||||
"docs": "https://support.pagerduty.com/main/docs/pagerduty-mcp-server"
|
||||
},
|
||||
"warnings": [
|
||||
"A PagerDuty API token; choose the regional endpoint that hosts the account."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "planetscale",
|
||||
"name": "PlanetScale",
|
||||
"description": "Connect PlanetScale's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"data"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/planetscale.svg",
|
||||
"darkLogoUrl": "/brands/apps/planetscale-dark.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.pscale.dev/*"
|
||||
],
|
||||
"docsUrl": "https://planetscale.com/docs/connect/mcp",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.pscale.dev/mcp/planetscale"
|
||||
},
|
||||
"guidanceMd": "Connect PlanetScale in the browser. A PlanetScale account; database and branch access are chosen during authorization.",
|
||||
"riskTier": "S4",
|
||||
"label": "Database access",
|
||||
"consoleLinks": {
|
||||
"docs": "https://planetscale.com/docs/connect/mcp"
|
||||
},
|
||||
"warnings": [
|
||||
"A PlanetScale account; database and branch access are chosen during authorization."
|
||||
],
|
||||
"tenantFields": [
|
||||
{
|
||||
"key": "project",
|
||||
"label": "Project or database",
|
||||
"type": "text",
|
||||
"advanced": true,
|
||||
"placeholder": "Optional project or database name",
|
||||
"helperMd": "Records the intended database boundary; final access is selected during PlanetScale authorization."
|
||||
},
|
||||
{
|
||||
"key": "branch",
|
||||
"label": "Branch",
|
||||
"type": "text",
|
||||
"advanced": true,
|
||||
"placeholder": "Optional branch name",
|
||||
"helperMd": "Records the intended branch boundary; final access is selected during PlanetScale authorization."
|
||||
}
|
||||
],
|
||||
"requiredResourceFilters": [
|
||||
"organization",
|
||||
"database",
|
||||
"branch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "mcp-insights-only",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use query insights and schema recommendations without query execution tools.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.pscale.dev/mcp/planetscale-insights-only"
|
||||
},
|
||||
"guidanceMd": "Connect PlanetScale in the browser. A PlanetScale account; database and branch access are chosen during authorization.",
|
||||
"riskTier": "S4",
|
||||
"label": "Insights only",
|
||||
"consoleLinks": {
|
||||
"docs": "https://planetscale.com/docs/connect/mcp"
|
||||
},
|
||||
"warnings": [
|
||||
"A PlanetScale account; database and branch access are chosen during authorization."
|
||||
],
|
||||
"requiredResourceFilters": [
|
||||
"organization",
|
||||
"database",
|
||||
"branch"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -2,13 +2,14 @@
|
|||
"schemaVersion": 1,
|
||||
"slug": "posthog",
|
||||
"name": "PostHog",
|
||||
"description": "Analyze product usage, errors, feature flags, and experiments in a pinned PostHog project.",
|
||||
"description": "Analyze product usage, errors, feature flags, and experiments with PostHog's hosted MCP server.",
|
||||
"categories": [
|
||||
"analytics"
|
||||
],
|
||||
"featured": true,
|
||||
"branding": {
|
||||
"logoUrl": "https://www.google.com/s2/favicons?domain=posthog.com&sz=128"
|
||||
"logoUrl": "/brands/apps/posthog.svg",
|
||||
"darkLogoUrl": "/brands/apps/posthog-dark.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.posthog.com/*"
|
||||
|
|
@ -26,16 +27,16 @@
|
|||
"defaults": {
|
||||
"serverUrl": "https://mcp.posthog.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Pin the connection to one PostHog project and expose the full tool catalog by default. Narrow feature groups or tools only when needed.",
|
||||
"guidanceMd": "Connect with PostHog's recommended defaults. Project pinning, read-only access, and catalog filters are optional advanced controls.",
|
||||
"riskTier": "S3",
|
||||
"tenantFields": [
|
||||
{
|
||||
"key": "projectId",
|
||||
"label": "Project ID",
|
||||
"label": "Pin to project ID",
|
||||
"type": "text",
|
||||
"required": true,
|
||||
"placeholder": "12345",
|
||||
"helperMd": "Find the numeric project ID in PostHog project settings.",
|
||||
"advanced": true,
|
||||
"placeholder": "Optional numeric project ID",
|
||||
"helperMd": "Optional. Pin this connection to one project and remove PostHog's project-switching tool.",
|
||||
"validation": {
|
||||
"pattern": "^[0-9]+$",
|
||||
"maxLength": 32
|
||||
|
|
@ -49,6 +50,7 @@
|
|||
"key": "readOnly",
|
||||
"label": "Read-only mode",
|
||||
"type": "checkbox",
|
||||
"advanced": true,
|
||||
"defaultValue": false,
|
||||
"helperMd": "Turn on to hide tools that can change PostHog data.",
|
||||
"transport": {
|
||||
|
|
@ -94,7 +96,7 @@
|
|||
"key": "mode",
|
||||
"label": "Tool response mode",
|
||||
"type": "select",
|
||||
"advanced": true,
|
||||
"hidden": true,
|
||||
"required": true,
|
||||
"placeholder": "Individual tools",
|
||||
"defaultValue": "tools",
|
||||
|
|
@ -111,12 +113,27 @@
|
|||
}
|
||||
}
|
||||
],
|
||||
"requiredResourceFilters": [
|
||||
"project"
|
||||
],
|
||||
"label": "Sign in with PostHog",
|
||||
"consoleLinks": {
|
||||
"docs": "https://posthog.com/docs/model-context-protocol"
|
||||
},
|
||||
"credentialSources": {
|
||||
"vercelConnect": {
|
||||
"services": [
|
||||
"posthog",
|
||||
"mcp.posthog.com/mcp"
|
||||
],
|
||||
"principalModes": [
|
||||
"user"
|
||||
],
|
||||
"scopes": [
|
||||
"*"
|
||||
],
|
||||
"header": {
|
||||
"name": "Authorization",
|
||||
"prefix": "Bearer "
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -130,16 +147,16 @@
|
|||
"defaults": {
|
||||
"serverUrl": "https://mcp.posthog.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Pin the connection to one PostHog project and expose the full tool catalog by default. Narrow feature groups or tools only when needed.",
|
||||
"guidanceMd": "Connect with PostHog's recommended defaults. Project pinning, read-only access, and catalog filters are optional advanced controls.",
|
||||
"riskTier": "S3",
|
||||
"tenantFields": [
|
||||
{
|
||||
"key": "projectId",
|
||||
"label": "Project ID",
|
||||
"label": "Pin to project ID",
|
||||
"type": "text",
|
||||
"required": true,
|
||||
"placeholder": "12345",
|
||||
"helperMd": "Find the numeric project ID in PostHog project settings.",
|
||||
"advanced": true,
|
||||
"placeholder": "Optional numeric project ID",
|
||||
"helperMd": "Optional. Pin this connection to one project and remove PostHog's project-switching tool.",
|
||||
"validation": {
|
||||
"pattern": "^[0-9]+$",
|
||||
"maxLength": 32
|
||||
|
|
@ -153,6 +170,7 @@
|
|||
"key": "readOnly",
|
||||
"label": "Read-only mode",
|
||||
"type": "checkbox",
|
||||
"advanced": true,
|
||||
"defaultValue": false,
|
||||
"helperMd": "Turn on to hide tools that can change PostHog data.",
|
||||
"transport": {
|
||||
|
|
@ -198,7 +216,7 @@
|
|||
"key": "mode",
|
||||
"label": "Tool response mode",
|
||||
"type": "select",
|
||||
"advanced": true,
|
||||
"hidden": true,
|
||||
"required": true,
|
||||
"placeholder": "Individual tools",
|
||||
"defaultValue": "tools",
|
||||
|
|
@ -215,9 +233,6 @@
|
|||
}
|
||||
}
|
||||
],
|
||||
"requiredResourceFilters": [
|
||||
"project"
|
||||
],
|
||||
"label": "Use a personal API key",
|
||||
"credentialFields": [
|
||||
{
|
||||
|
|
@ -237,7 +252,27 @@
|
|||
"consoleLinks": {
|
||||
"keys": "https://posthog.com/docs/model-context-protocol/faq",
|
||||
"docs": "https://posthog.com/docs/model-context-protocol/faq"
|
||||
},
|
||||
"credentialSources": {
|
||||
"vercelConnect": {
|
||||
"services": [
|
||||
"posthog",
|
||||
"mcp.posthog.com/mcp"
|
||||
],
|
||||
"principalModes": [
|
||||
"app"
|
||||
],
|
||||
"scopes": [
|
||||
"*"
|
||||
],
|
||||
"header": {
|
||||
"name": "Authorization",
|
||||
"prefix": "Bearer "
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"docsUrl": "https://posthog.com/docs/model-context-protocol",
|
||||
"redirectConstraints": "https-or-loopback-http"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,194 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "postman",
|
||||
"name": "Postman",
|
||||
"description": "Connect Postman's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"developer"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/postman.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.postman.com/*"
|
||||
],
|
||||
"docsUrl": "https://learning.postman.com/latest-v-12/docs/reference/postman-api/postman-mcp-server/postman-mcp-remote-server",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth-minimal",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.postman.com/minimal"
|
||||
},
|
||||
"guidanceMd": "Connect Postman in the browser. A Postman account; OAuth is available for US endpoints and API keys are required for EU endpoints.",
|
||||
"riskTier": "S3",
|
||||
"label": "US · Minimal",
|
||||
"consoleLinks": {
|
||||
"docs": "https://learning.postman.com/latest-v-12/docs/reference/postman-api/postman-mcp-server/postman-mcp-remote-server"
|
||||
},
|
||||
"warnings": [
|
||||
"A Postman account; OAuth is available for US endpoints and API keys are required for EU endpoints."
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "mcp-oauth-code",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.postman.com/code"
|
||||
},
|
||||
"guidanceMd": "Connect Postman in the browser. A Postman account; OAuth is available for US endpoints and API keys are required for EU endpoints.",
|
||||
"riskTier": "S3",
|
||||
"label": "US · Code",
|
||||
"consoleLinks": {
|
||||
"docs": "https://learning.postman.com/latest-v-12/docs/reference/postman-api/postman-mcp-server/postman-mcp-remote-server"
|
||||
},
|
||||
"warnings": [
|
||||
"A Postman account; OAuth is available for US endpoints and API keys are required for EU endpoints."
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "mcp-oauth-full",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.postman.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Postman in the browser. A Postman account; OAuth is available for US endpoints and API keys are required for EU endpoints.",
|
||||
"riskTier": "S3",
|
||||
"label": "US · Full",
|
||||
"consoleLinks": {
|
||||
"docs": "https://learning.postman.com/latest-v-12/docs/reference/postman-api/postman-mcp-server/postman-mcp-remote-server"
|
||||
},
|
||||
"warnings": [
|
||||
"A Postman account; OAuth is available for US endpoints and API keys are required for EU endpoints."
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "mcp-eu-key-minimal",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "api_key",
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a restricted customer-owned key when browser sign-in is not suitable.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.eu.postman.com/minimal"
|
||||
},
|
||||
"guidanceMd": "Use a customer-created Postman key. A Postman account; OAuth is available for US endpoints and API keys are required for EU endpoints.",
|
||||
"riskTier": "S3",
|
||||
"label": "EU · Minimal",
|
||||
"credentialFields": [
|
||||
{
|
||||
"key": "authorization",
|
||||
"label": "Postman API key",
|
||||
"type": "password",
|
||||
"required": true,
|
||||
"placeholder": "PMAK-...",
|
||||
"secret": true
|
||||
}
|
||||
],
|
||||
"keyPlacement": {
|
||||
"location": "header",
|
||||
"name": "X-API-Key",
|
||||
"prefix": null
|
||||
},
|
||||
"consoleLinks": {
|
||||
"keys": "https://learning.postman.com/latest-v-12/docs/reference/postman-api/postman-mcp-server/postman-mcp-remote-server",
|
||||
"docs": "https://learning.postman.com/latest-v-12/docs/reference/postman-api/postman-mcp-server/postman-mcp-remote-server"
|
||||
},
|
||||
"warnings": [
|
||||
"A Postman account; OAuth is available for US endpoints and API keys are required for EU endpoints."
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "mcp-eu-key-code",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "api_key",
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a restricted customer-owned key when browser sign-in is not suitable.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.eu.postman.com/code"
|
||||
},
|
||||
"guidanceMd": "Use a customer-created Postman key. A Postman account; OAuth is available for US endpoints and API keys are required for EU endpoints.",
|
||||
"riskTier": "S3",
|
||||
"label": "EU · Code",
|
||||
"credentialFields": [
|
||||
{
|
||||
"key": "authorization",
|
||||
"label": "Postman API key",
|
||||
"type": "password",
|
||||
"required": true,
|
||||
"placeholder": "PMAK-...",
|
||||
"secret": true
|
||||
}
|
||||
],
|
||||
"keyPlacement": {
|
||||
"location": "header",
|
||||
"name": "X-API-Key",
|
||||
"prefix": null
|
||||
},
|
||||
"consoleLinks": {
|
||||
"keys": "https://learning.postman.com/latest-v-12/docs/reference/postman-api/postman-mcp-server/postman-mcp-remote-server",
|
||||
"docs": "https://learning.postman.com/latest-v-12/docs/reference/postman-api/postman-mcp-server/postman-mcp-remote-server"
|
||||
},
|
||||
"warnings": [
|
||||
"A Postman account; OAuth is available for US endpoints and API keys are required for EU endpoints."
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "mcp-eu-key-full",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "api_key",
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a restricted customer-owned key when browser sign-in is not suitable.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.eu.postman.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Use a customer-created Postman key. A Postman account; OAuth is available for US endpoints and API keys are required for EU endpoints.",
|
||||
"riskTier": "S3",
|
||||
"label": "EU · Full",
|
||||
"credentialFields": [
|
||||
{
|
||||
"key": "authorization",
|
||||
"label": "Postman API key",
|
||||
"type": "password",
|
||||
"required": true,
|
||||
"placeholder": "PMAK-...",
|
||||
"secret": true
|
||||
}
|
||||
],
|
||||
"keyPlacement": {
|
||||
"location": "header",
|
||||
"name": "X-API-Key",
|
||||
"prefix": null
|
||||
},
|
||||
"consoleLinks": {
|
||||
"keys": "https://learning.postman.com/latest-v-12/docs/reference/postman-api/postman-mcp-server/postman-mcp-remote-server",
|
||||
"docs": "https://learning.postman.com/latest-v-12/docs/reference/postman-api/postman-mcp-server/postman-mcp-remote-server"
|
||||
},
|
||||
"warnings": [
|
||||
"A Postman account; OAuth is available for US endpoints and API keys are required for EU endpoints."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "razorpay",
|
||||
"name": "Razorpay",
|
||||
"description": "Connect Razorpay's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"commerce"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/razorpay.svg",
|
||||
"darkLogoUrl": "/brands/apps/razorpay-dark.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.razorpay.com/*"
|
||||
],
|
||||
"docsUrl": "https://razorpay.com/docs/mcp-server/oauth/",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.razorpay.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Razorpay in the browser. A Razorpay account; financial or destructive actions always require explicit approval.",
|
||||
"riskTier": "S4",
|
||||
"label": "Sign in with Razorpay",
|
||||
"consoleLinks": {
|
||||
"docs": "https://razorpay.com/docs/mcp-server/oauth/"
|
||||
},
|
||||
"warnings": [
|
||||
"A Razorpay account; financial or destructive actions always require explicit approval.",
|
||||
"Financial or destructive actions must be explicitly approved before execution."
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "mcp-api-key",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "api_key",
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a restricted customer-owned key when browser sign-in is not suitable.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.razorpay.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Use a customer-created Razorpay key. A Razorpay account; financial or destructive actions always require explicit approval.",
|
||||
"riskTier": "S4",
|
||||
"label": "Use an API key",
|
||||
"credentialFields": [
|
||||
{
|
||||
"key": "authorization",
|
||||
"label": "Razorpay API key",
|
||||
"type": "password",
|
||||
"required": true,
|
||||
"placeholder": "Paste the base64-encoded key ID and secret",
|
||||
"secret": true
|
||||
}
|
||||
],
|
||||
"keyPlacement": {
|
||||
"location": "header",
|
||||
"name": "Authorization",
|
||||
"prefix": "Basic "
|
||||
},
|
||||
"consoleLinks": {
|
||||
"keys": "https://razorpay.com/docs/mcp-server/oauth/",
|
||||
"docs": "https://razorpay.com/docs/mcp-server/oauth/"
|
||||
},
|
||||
"warnings": [
|
||||
"A Razorpay account; financial or destructive actions always require explicit approval.",
|
||||
"Financial or destructive actions must be explicitly approved before execution."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "resend",
|
||||
"name": "Resend",
|
||||
"description": "Connect Resend's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"communication"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/resend.svg",
|
||||
"darkLogoUrl": "/brands/apps/resend-dark.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.resend.com/*"
|
||||
],
|
||||
"docsUrl": "https://resend.com/changelog/remote-mcp-server",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.resend.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Resend in the browser. A Resend account with access to the relevant domains.",
|
||||
"riskTier": "S3",
|
||||
"label": "Sign in with Resend",
|
||||
"consoleLinks": {
|
||||
"docs": "https://resend.com/changelog/remote-mcp-server"
|
||||
},
|
||||
"warnings": [
|
||||
"A Resend account with access to the relevant domains."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "sanity",
|
||||
"name": "Sanity",
|
||||
"description": "Connect Sanity's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"content"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/sanity.svg",
|
||||
"darkLogoUrl": "/brands/apps/sanity-dark.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.sanity.io/*"
|
||||
],
|
||||
"docsUrl": "https://www.sanity.io/docs/ai/mcp-server",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.sanity.io"
|
||||
},
|
||||
"guidanceMd": "Connect Sanity in the browser. A Sanity account with access to the relevant projects and datasets.",
|
||||
"riskTier": "S3",
|
||||
"label": "Sign in with Sanity",
|
||||
"consoleLinks": {
|
||||
"docs": "https://www.sanity.io/docs/ai/mcp-server"
|
||||
},
|
||||
"warnings": [
|
||||
"A Sanity account with access to the relevant projects and datasets."
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "mcp-api-key",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "api_key",
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a restricted customer-owned key when browser sign-in is not suitable.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.sanity.io"
|
||||
},
|
||||
"guidanceMd": "Use a customer-created Sanity key. A Sanity account with access to the relevant projects and datasets.",
|
||||
"riskTier": "S3",
|
||||
"label": "Use an API key",
|
||||
"credentialFields": [
|
||||
{
|
||||
"key": "authorization",
|
||||
"label": "Sanity API key",
|
||||
"type": "password",
|
||||
"required": true,
|
||||
"placeholder": "sk...",
|
||||
"secret": true
|
||||
}
|
||||
],
|
||||
"keyPlacement": {
|
||||
"location": "header",
|
||||
"name": "Authorization",
|
||||
"prefix": "Bearer "
|
||||
},
|
||||
"consoleLinks": {
|
||||
"keys": "https://www.sanity.io/docs/ai/mcp-server",
|
||||
"docs": "https://www.sanity.io/docs/ai/mcp-server"
|
||||
},
|
||||
"warnings": [
|
||||
"A Sanity account with access to the relevant projects and datasets."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -8,7 +8,8 @@
|
|||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "https://www.google.com/s2/favicons?domain=sentry.io&sz=128"
|
||||
"logoUrl": "/brands/apps/sentry.svg",
|
||||
"darkLogoUrl": "/brands/apps/sentry-dark.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.sentry.dev/*"
|
||||
|
|
@ -35,5 +36,7 @@
|
|||
"environment"
|
||||
]
|
||||
}
|
||||
]
|
||||
],
|
||||
"docsUrl": "https://mcp.sentry.dev/.well-known/oauth-authorization-server",
|
||||
"redirectConstraints": "https-or-loopback-http"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "shopify",
|
||||
"name": "Shopify",
|
||||
"description": "Search a store's products and policies, and manage shopping carts.",
|
||||
"categories": [
|
||||
"commerce"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/shopify.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://*.myshopify.com/api/mcp"
|
||||
],
|
||||
"methods": [
|
||||
{
|
||||
"key": "storefront-mcp",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "none",
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a store's public myshopify.com domain. No Shopify app or OAuth registration is required.",
|
||||
"defaults": {
|
||||
"serverUrlTemplate": "https://{storeDomain}/api/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Shopify's official Storefront MCP server for shopper-facing catalog, policy, and cart tools.",
|
||||
"riskTier": "S3",
|
||||
"label": "Connect a Shopify storefront",
|
||||
"tenantFields": [
|
||||
{
|
||||
"key": "storeDomain",
|
||||
"label": "Store domain",
|
||||
"type": "text",
|
||||
"required": true,
|
||||
"placeholder": "your-store.myshopify.com",
|
||||
"helperMd": "Enter the full myshopify.com domain without https://.",
|
||||
"validation": {
|
||||
"pattern": "^[A-Za-z0-9][A-Za-z0-9-]*\\.myshopify\\.com$",
|
||||
"maxLength": 255
|
||||
}
|
||||
}
|
||||
],
|
||||
"consoleLinks": {
|
||||
"docs": "https://shopify.dev/docs/apps/build/storefront-mcp/servers/storefront"
|
||||
},
|
||||
"warnings": [
|
||||
"This is Shopify's Storefront MCP, not Admin API access. It does not manage merchant products, orders, or customers.",
|
||||
"The storefront must be publicly reachable. Password-protected or restricted trial stores can return HTTP 401."
|
||||
],
|
||||
"requiredResourceFilters": [
|
||||
"store"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "similarweb",
|
||||
"name": "Similarweb",
|
||||
"description": "Connect Similarweb's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"analytics"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/similarweb.svg",
|
||||
"darkLogoUrl": "/brands/apps/similarweb-dark.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.similarweb.com/*"
|
||||
],
|
||||
"docsUrl": "https://developers.similarweb.com/docs/similarweb-mcp",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-api-key",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "api_key",
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a restricted customer-owned key when browser sign-in is not suitable.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.similarweb.com"
|
||||
},
|
||||
"guidanceMd": "Use a customer-created Similarweb key. A Similarweb subscription with API access and an API key.",
|
||||
"riskTier": "S2",
|
||||
"label": "Use an API key",
|
||||
"credentialFields": [
|
||||
{
|
||||
"key": "authorization",
|
||||
"label": "Similarweb API key",
|
||||
"type": "password",
|
||||
"required": true,
|
||||
"placeholder": "Paste your Similarweb API key",
|
||||
"secret": true
|
||||
}
|
||||
],
|
||||
"keyPlacement": {
|
||||
"location": "header",
|
||||
"name": "api-key",
|
||||
"prefix": null
|
||||
},
|
||||
"consoleLinks": {
|
||||
"keys": "https://developers.similarweb.com/docs/similarweb-mcp",
|
||||
"docs": "https://developers.similarweb.com/docs/similarweb-mcp"
|
||||
},
|
||||
"warnings": [
|
||||
"A Similarweb subscription with API access and an API key."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
],
|
||||
"featured": true,
|
||||
"branding": {
|
||||
"logoUrl": "https://www.google.com/s2/favicons?domain=slack.com&sz=128"
|
||||
"logoUrl": "/brands/apps/slack.png"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.slack.com/*"
|
||||
|
|
@ -19,8 +19,7 @@
|
|||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"customer",
|
||||
"dcr"
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use the provider-hosted connection for the quickest setup.",
|
||||
"defaults": {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "stripe",
|
||||
"name": "Stripe",
|
||||
"description": "Connect Stripe's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"commerce"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/stripe.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.stripe.com/*"
|
||||
],
|
||||
"docsUrl": "https://docs.stripe.com/mcp",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.stripe.com"
|
||||
},
|
||||
"guidanceMd": "Connect Stripe in the browser. A Stripe account; the server is public preview and payment actions require explicit approval.",
|
||||
"riskTier": "S4",
|
||||
"label": "Sign in with Stripe",
|
||||
"consoleLinks": {
|
||||
"docs": "https://docs.stripe.com/mcp"
|
||||
},
|
||||
"warnings": [
|
||||
"A Stripe account; the server is public preview and payment actions require explicit approval.",
|
||||
"Financial or destructive actions must be explicitly approved before execution."
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "mcp-api-key",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "api_key",
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a restricted customer-owned key when browser sign-in is not suitable.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.stripe.com"
|
||||
},
|
||||
"guidanceMd": "Use a customer-created Stripe key. A Stripe account; the server is public preview and payment actions require explicit approval.",
|
||||
"riskTier": "S4",
|
||||
"label": "Use an API key",
|
||||
"credentialFields": [
|
||||
{
|
||||
"key": "authorization",
|
||||
"label": "Stripe API key",
|
||||
"type": "password",
|
||||
"required": true,
|
||||
"placeholder": "sk_...",
|
||||
"secret": true
|
||||
}
|
||||
],
|
||||
"keyPlacement": {
|
||||
"location": "header",
|
||||
"name": "Authorization",
|
||||
"prefix": "Bearer "
|
||||
},
|
||||
"consoleLinks": {
|
||||
"keys": "https://docs.stripe.com/mcp",
|
||||
"docs": "https://docs.stripe.com/mcp"
|
||||
},
|
||||
"warnings": [
|
||||
"A Stripe account; the server is public preview and payment actions require explicit approval.",
|
||||
"Financial or destructive actions must be explicitly approved before execution."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "supabase",
|
||||
"name": "Supabase",
|
||||
"description": "Connect Supabase's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"data"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/supabase.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.supabase.com/*"
|
||||
],
|
||||
"docsUrl": "https://supabase.com/docs/guides/ai-tools/mcp",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.supabase.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Supabase in the browser and scope the connection to one development project. Write tools start enabled and remain governed by Paperclip's action policies.",
|
||||
"riskTier": "S4",
|
||||
"label": "Sign in with Supabase",
|
||||
"consoleLinks": {
|
||||
"docs": "https://supabase.com/docs/guides/ai-tools/mcp"
|
||||
},
|
||||
"warnings": [
|
||||
"A Supabase account; use a development project and review write actions before connecting production data.",
|
||||
"Do not connect production data unless you have reviewed Supabase's MCP security guidance."
|
||||
],
|
||||
"tenantFields": [
|
||||
{
|
||||
"key": "projectRef",
|
||||
"label": "Project reference",
|
||||
"type": "text",
|
||||
"required": true,
|
||||
"placeholder": "abcdefghijklmnopqrst",
|
||||
"helperMd": "Scope the connection to one development project.",
|
||||
"transport": {
|
||||
"location": "query",
|
||||
"name": "project_ref"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "readOnly",
|
||||
"label": "Read-only mode",
|
||||
"type": "checkbox",
|
||||
"defaultValue": false,
|
||||
"helperMd": "Enable this to prevent the connection from changing the database.",
|
||||
"transport": {
|
||||
"location": "query",
|
||||
"name": "read_only",
|
||||
"format": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "features",
|
||||
"label": "Feature groups",
|
||||
"type": "textarea",
|
||||
"advanced": true,
|
||||
"placeholder": "database,docs",
|
||||
"helperMd": "Optional comma-separated feature groups.",
|
||||
"transport": {
|
||||
"location": "query",
|
||||
"name": "features",
|
||||
"format": "csv"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requiredResourceFilters": [
|
||||
"project"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "mcp-api-key",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "api_key",
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use a restricted customer-owned key when browser sign-in is not suitable.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.supabase.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Use a customer-created Supabase key scoped to one development project. Write tools start enabled and remain governed by Paperclip's action policies.",
|
||||
"riskTier": "S4",
|
||||
"label": "Use an API key",
|
||||
"credentialFields": [
|
||||
{
|
||||
"key": "authorization",
|
||||
"label": "Supabase API key",
|
||||
"type": "password",
|
||||
"required": true,
|
||||
"placeholder": "sbp_...",
|
||||
"secret": true
|
||||
}
|
||||
],
|
||||
"keyPlacement": {
|
||||
"location": "header",
|
||||
"name": "Authorization",
|
||||
"prefix": "Bearer "
|
||||
},
|
||||
"consoleLinks": {
|
||||
"keys": "https://supabase.com/docs/guides/ai-tools/mcp",
|
||||
"docs": "https://supabase.com/docs/guides/ai-tools/mcp"
|
||||
},
|
||||
"warnings": [
|
||||
"A Supabase account; use a development project and review write actions before connecting production data.",
|
||||
"Do not connect production data unless you have reviewed Supabase's MCP security guidance."
|
||||
],
|
||||
"tenantFields": [
|
||||
{
|
||||
"key": "projectRef",
|
||||
"label": "Project reference",
|
||||
"type": "text",
|
||||
"required": true,
|
||||
"placeholder": "abcdefghijklmnopqrst",
|
||||
"helperMd": "Scope the connection to one development project.",
|
||||
"transport": {
|
||||
"location": "query",
|
||||
"name": "project_ref"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "readOnly",
|
||||
"label": "Read-only mode",
|
||||
"type": "checkbox",
|
||||
"defaultValue": false,
|
||||
"helperMd": "Enable this to prevent the connection from changing the database.",
|
||||
"transport": {
|
||||
"location": "query",
|
||||
"name": "read_only",
|
||||
"format": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "features",
|
||||
"label": "Feature groups",
|
||||
"type": "textarea",
|
||||
"advanced": true,
|
||||
"placeholder": "database,docs",
|
||||
"helperMd": "Optional comma-separated feature groups.",
|
||||
"transport": {
|
||||
"location": "query",
|
||||
"name": "features",
|
||||
"format": "csv"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requiredResourceFilters": [
|
||||
"project"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "ticket-tailor",
|
||||
"name": "Ticket Tailor",
|
||||
"description": "Connect Ticket Tailor's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"commerce"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/ticket-tailor.svg",
|
||||
"darkLogoUrl": "/brands/apps/ticket-tailor-dark.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.tickettailor.ai/*"
|
||||
],
|
||||
"docsUrl": "https://developers.tickettailor.com/docs/mcp/authentication/",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.tickettailor.ai/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Ticket Tailor in the browser. A Ticket Tailor account; the provider may request an API key during its hosted authorization prompt.",
|
||||
"riskTier": "S3",
|
||||
"label": "Sign in with Ticket Tailor",
|
||||
"consoleLinks": {
|
||||
"docs": "https://developers.tickettailor.com/docs/mcp/authentication/"
|
||||
},
|
||||
"warnings": [
|
||||
"A Ticket Tailor account; the provider may request an API key during its hosted authorization prompt."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "ticktick",
|
||||
"name": "TickTick",
|
||||
"description": "Connect TickTick's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"productivity"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/ticktick.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.ticktick.com/*"
|
||||
],
|
||||
"docsUrl": "https://help.ticktick.com/articles/7438129581631995904",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.ticktick.com"
|
||||
},
|
||||
"guidanceMd": "Connect TickTick in the browser. A TickTick account with MCP access.",
|
||||
"riskTier": "S3",
|
||||
"label": "Sign in with TickTick",
|
||||
"consoleLinks": {
|
||||
"docs": "https://help.ticktick.com/articles/7438129581631995904"
|
||||
},
|
||||
"warnings": [
|
||||
"A TickTick account with MCP access."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "todoist",
|
||||
"name": "Todoist",
|
||||
"description": "Connect Todoist's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"productivity"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/todoist.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://ai.todoist.net/*"
|
||||
],
|
||||
"docsUrl": "https://developer.todoist.com/",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://ai.todoist.net/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Todoist in the browser. A Todoist account.",
|
||||
"riskTier": "S3",
|
||||
"label": "Sign in with Todoist",
|
||||
"consoleLinks": {
|
||||
"docs": "https://developer.todoist.com/"
|
||||
},
|
||||
"warnings": [
|
||||
"A Todoist account."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -8,7 +8,8 @@
|
|||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "https://www.google.com/s2/favicons?domain=vercel.com&sz=128"
|
||||
"logoUrl": "/brands/apps/vercel.svg",
|
||||
"darkLogoUrl": "/brands/apps/vercel-dark.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.vercel.com/*"
|
||||
|
|
@ -34,5 +35,9 @@
|
|||
"environment"
|
||||
]
|
||||
}
|
||||
]
|
||||
],
|
||||
"availability": {
|
||||
"available": false,
|
||||
"reason": "Vercel currently reviews and approves MCP clients."
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "webflow",
|
||||
"name": "Webflow",
|
||||
"description": "Connect Webflow's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"content"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/webflow.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.webflow.com/*"
|
||||
],
|
||||
"docsUrl": "https://developers.webflow.com/mcp/reference/getting-started",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.webflow.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Webflow in the browser. A Webflow account; workspace and site roles constrain accessible sites.",
|
||||
"riskTier": "S3",
|
||||
"label": "Sign in with Webflow",
|
||||
"consoleLinks": {
|
||||
"docs": "https://developers.webflow.com/mcp/reference/getting-started"
|
||||
},
|
||||
"warnings": [
|
||||
"A Webflow account; workspace and site roles constrain accessible sites."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "wix",
|
||||
"name": "Wix",
|
||||
"description": "Connect Wix's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"content"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/wix.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.wix.com/*"
|
||||
],
|
||||
"docsUrl": "https://www.wix.com/studio/developers/mcp-server",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"dcr"
|
||||
],
|
||||
"whenToUse": "Use browser sign-in for the provider-hosted MCP server.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.wix.com/mcp"
|
||||
},
|
||||
"guidanceMd": "Connect Wix in the browser. A Wix account with access to the relevant sites.",
|
||||
"riskTier": "S3",
|
||||
"label": "Sign in with Wix",
|
||||
"consoleLinks": {
|
||||
"docs": "https://www.wix.com/studio/developers/mcp-server"
|
||||
},
|
||||
"warnings": [
|
||||
"A Wix account with access to the relevant sites."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "xero",
|
||||
"name": "Xero",
|
||||
"description": "Connect Xero's provider-hosted MCP server.",
|
||||
"categories": [
|
||||
"commerce"
|
||||
],
|
||||
"featured": false,
|
||||
"branding": {
|
||||
"logoUrl": "/brands/apps/xero.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.xero.com/*"
|
||||
],
|
||||
"docsUrl": "https://developer.xero.com/ai",
|
||||
"redirectConstraints": "https-or-loopback-http",
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-own-oauth",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "oauth",
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Register an OAuth app with Xero, then enter its client ID and secret.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.xero.com/mcp",
|
||||
"scopesHint": [
|
||||
"openid",
|
||||
"profile",
|
||||
"email",
|
||||
"offline_access",
|
||||
"accounting.settings",
|
||||
"accounting.invoices.read",
|
||||
"accounting.reports.aged.read",
|
||||
"accounting.reports.balancesheet.read",
|
||||
"accounting.reports.profitandloss.read"
|
||||
]
|
||||
},
|
||||
"guidanceMd": "Connect Xero in the browser. Create a Xero OAuth app and confirm the applicable AI and data-use terms before connecting.",
|
||||
"riskTier": "S4",
|
||||
"label": "Use your own OAuth app",
|
||||
"consoleLinks": {
|
||||
"register": "https://developer.xero.com/ai",
|
||||
"docs": "https://developer.xero.com/ai"
|
||||
},
|
||||
"warnings": [
|
||||
"Create a Xero OAuth app and confirm the applicable AI and data-use terms before connecting."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -8,40 +8,25 @@
|
|||
],
|
||||
"featured": true,
|
||||
"branding": {
|
||||
"logoUrl": "https://www.google.com/s2/favicons?domain=zapier.com&sz=128"
|
||||
"logoUrl": "/brands/apps/zapier.svg"
|
||||
},
|
||||
"urlPatterns": [
|
||||
"https://mcp.zapier.com/*"
|
||||
],
|
||||
"methods": [
|
||||
{
|
||||
"key": "mcp-key",
|
||||
"key": "generated-url",
|
||||
"transport": "mcp_remote",
|
||||
"auth": "api_key",
|
||||
"auth": "none",
|
||||
"ownershipModes": [
|
||||
"customer"
|
||||
],
|
||||
"whenToUse": "Use the provider-hosted connection for the quickest setup.",
|
||||
"defaults": {
|
||||
"serverUrl": "https://mcp.zapier.com/api/mcp"
|
||||
},
|
||||
"guidanceMd": "Create a Zapier MCP connection, then paste its token here.",
|
||||
"whenToUse": "Use the complete provider-generated MCP URL from Zapier.",
|
||||
"defaults": {},
|
||||
"guidanceMd": "Create a Zapier MCP server, then paste the complete generated connection URL. The token remains embedded in that URL.",
|
||||
"riskTier": "S3",
|
||||
"credentialFields": [
|
||||
{
|
||||
"key": "authorization",
|
||||
"label": "Zapier MCP token",
|
||||
"type": "password",
|
||||
"required": true,
|
||||
"placeholder": "Paste your Zapier token",
|
||||
"secret": true
|
||||
}
|
||||
],
|
||||
"keyPlacement": {
|
||||
"location": "header",
|
||||
"name": "Authorization",
|
||||
"prefix": "Bearer "
|
||||
}
|
||||
"label": "Paste generated MCP URL"
|
||||
}
|
||||
]
|
||||
],
|
||||
"docsUrl": "https://docs.zapier.com/mcp/quickstart"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
export const GOOGLE_WORKSPACE_CONNECTOR_PROFILE_IDS = [
|
||||
"gmail.read", "gmail.draft", "drive.read", "drive.write", "docs.read", "docs.write",
|
||||
"sheets.read", "sheets.write", "slides.read", "slides.write", "calendar.read",
|
||||
"calendar.write", "chat.read", "chat.write", "people.read", "workspace-search.read",
|
||||
] as const;
|
||||
|
||||
export type GoogleWorkspaceConnectorProfileId = (typeof GOOGLE_WORKSPACE_CONNECTOR_PROFILE_IDS)[number];
|
||||
|
||||
const auth = (scope: string) => `https://www.googleapis.com/auth/${scope}`;
|
||||
|
||||
export const GOOGLE_WORKSPACE_CONNECTOR_PROFILES: Readonly<Record<GoogleWorkspaceConnectorProfileId, {
|
||||
appSlug: string;
|
||||
serverUrl: string;
|
||||
scopes: readonly string[];
|
||||
writeTools: readonly string[];
|
||||
}>> = {
|
||||
"gmail.read": def("gmail", "https://gmailmcp.googleapis.com/mcp/v1", [auth("gmail.readonly")]),
|
||||
"gmail.draft": def("gmail", "https://gmailmcp.googleapis.com/mcp/v1", [auth("gmail.readonly"), auth("gmail.compose")], ["create_draft"]),
|
||||
"drive.read": def("google-drive", "https://drivemcp.googleapis.com/mcp/v1", [auth("drive.readonly")]),
|
||||
"drive.write": def("google-drive", "https://drivemcp.googleapis.com/mcp/v1", [auth("drive.readonly"), auth("drive.file")], ["copy_file", "create_file"]),
|
||||
"docs.read": def("google-docs", "https://docsmcp.googleapis.com/mcp/v1", [auth("drive.readonly"), auth("documents.readonly")]),
|
||||
"docs.write": def("google-docs", "https://docsmcp.googleapis.com/mcp/v1", [auth("drive.readonly"), auth("drive.file"), auth("documents")], ["update_doc"]),
|
||||
"sheets.read": def("google-sheets", "https://sheetsmcp.googleapis.com/mcp/v1", [auth("drive.readonly"), auth("spreadsheets.readonly")]),
|
||||
"sheets.write": def("google-sheets", "https://sheetsmcp.googleapis.com/mcp/v1", [auth("drive.readonly"), auth("drive.file"), auth("spreadsheets")], ["update_spreadsheet", "update_values", "update_formulas", "insert_dimension"]),
|
||||
"slides.read": def("google-slides", "https://slidesmcp.googleapis.com/mcp/v1", [auth("drive.readonly"), auth("presentations.readonly")]),
|
||||
"slides.write": def("google-slides", "https://slidesmcp.googleapis.com/mcp/v1", [auth("drive.readonly"), auth("drive.file"), auth("presentations")], ["update_presentation"]),
|
||||
"calendar.read": def("google-calendar", "https://calendarmcp.googleapis.com/mcp/v1", [auth("calendar.calendarlist.readonly"), auth("calendar.events.freebusy"), auth("calendar.events.readonly")]),
|
||||
"calendar.write": def("google-calendar", "https://calendarmcp.googleapis.com/mcp/v1", [auth("calendar.calendarlist.readonly"), auth("calendar.events.freebusy"), auth("calendar.events")], ["create_event", "update_event", "delete_event", "respond_to_event"]),
|
||||
"chat.read": def("google-chat", "https://chatmcp.googleapis.com/mcp/v1", [auth("chat.spaces.readonly"), auth("chat.memberships.readonly"), auth("chat.messages.readonly"), auth("chat.users.readstate.readonly")]),
|
||||
"chat.write": def("google-chat", "https://chatmcp.googleapis.com/mcp/v1", [auth("chat.spaces.readonly"), auth("chat.memberships.readonly"), auth("chat.messages.readonly"), auth("chat.users.readstate.readonly"), auth("chat.messages.create")], ["send_message"]),
|
||||
"people.read": def("google-people", "https://people.googleapis.com/mcp/v1", [auth("directory.readonly"), auth("userinfo.profile"), auth("contacts.readonly")]),
|
||||
"workspace-search.read": def("google-workspace-search", "https://workspacemcp.googleapis.com/mcp/v1", [auth("gmail.readonly"), auth("drive.readonly"), auth("calendar.readonly"), auth("chat.messages.readonly")]),
|
||||
};
|
||||
|
||||
export function isGoogleWorkspaceConnectorProfileId(value: string): value is GoogleWorkspaceConnectorProfileId {
|
||||
return Object.prototype.hasOwnProperty.call(GOOGLE_WORKSPACE_CONNECTOR_PROFILES, value);
|
||||
}
|
||||
|
||||
function def(appSlug: string, serverUrl: string, scopes: readonly string[], writeTools: readonly string[] = []) {
|
||||
return { appSlug, serverUrl, scopes, writeTools };
|
||||
}
|
||||
|
|
@ -269,6 +269,7 @@ export {
|
|||
getAvailableConnectionMethods,
|
||||
getConnectableAppDefinition,
|
||||
recommendedDefaultsForApp,
|
||||
resolveConnectionMethodServerUrl,
|
||||
} from "./app-definitions.js";
|
||||
export { APP_DEFINITIONS } from "./app-definitions.generated.js";
|
||||
export * from "./validators/status-card.js";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"verifiedAt": "2026-08-26",
|
||||
"entries": [
|
||||
{ "slug": "jira", "name": "Jira", "wave": 1, "status": "self_serve", "docsUrl": "https://support.atlassian.com/atlassian-rovo-mcp-server/docs/getting-started-with-the-atlassian-remote-mcp-server/", "serverUrl": "https://mcp.atlassian.com/v1/mcp/authv2", "authMode": "dcr_cimd", "prerequisite": "An active Jira or Confluence site; tenant policy may require an administrator to approve the client.", "riskTier": "S3" },
|
||||
{ "slug": "airtable", "name": "Airtable", "wave": 1, "status": "self_serve", "docsUrl": "https://support.airtable.com/articles/9897799762-using-the-airtable-mcp-server", "serverUrl": "https://mcp.airtable.com/mcp", "authMode": "dcr", "prerequisite": "An Airtable account; enterprise administrators may need to allowlist the client.", "riskTier": "S3" },
|
||||
{ "slug": "beehiiv", "name": "beehiiv", "wave": 1, "status": "self_serve", "docsUrl": "https://www.beehiiv.com/features/mcp/getting-started", "serverUrl": "https://mcp.beehiiv.com/mcp", "authMode": "dcr", "prerequisite": "A beehiiv account; the subscription plan controls available write capabilities.", "riskTier": "S3" },
|
||||
{ "slug": "bitly", "name": "Bitly", "wave": 1, "status": "self_serve", "docsUrl": "https://dev.bitly.com/bitly-mcp/overview/quickstart/", "serverUrl": "https://api-ssl.bitly.com/v4/mcp", "authMode": "dcr_or_api_key", "prerequisite": "A Bitly account with either browser authorization or an API token.", "riskTier": "S2" },
|
||||
{ "slug": "candid", "name": "Candid", "wave": 1, "status": "self_serve", "docsUrl": "https://learning.candid.org/getting-started-with-the-candid-mcp-connector/375441", "serverUrl": "https://mcp.candid.org/mcp", "authMode": "dcr", "prerequisite": "A Candid account with access to the MCP connector.", "riskTier": "S2" },
|
||||
{ "slug": "cloudflare", "name": "Cloudflare", "wave": 1, "status": "self_serve", "docsUrl": "https://developers.cloudflare.com/agents/model-context-protocol/cloudflare/servers-for-cloudflare/", "serverUrl": "https://mcp.cloudflare.com/mcp", "authMode": "dcr_or_api_key", "prerequisite": "A Cloudflare account with access to the resources being connected.", "riskTier": "S3" },
|
||||
{ "slug": "cloudinary", "name": "Cloudinary", "wave": 1, "status": "self_serve", "docsUrl": "https://cloudinary.com/documentation/cloudinary_llm_mcp", "serverUrl": "https://asset-management.mcp.cloudinary.com/mcp", "authMode": "dcr", "prerequisite": "A Cloudinary account; authorization is limited by the signed-in user's roles.", "riskTier": "S3" },
|
||||
{ "slug": "coda", "name": "Coda", "wave": 1, "status": "self_serve", "docsUrl": "https://help.coda.io/hc/en-us/articles/44722769665549-Security-recommendations-for-the-Coda-MCP", "serverUrl": "https://coda.io/apis/mcp", "authMode": "dcr_or_api_key", "prerequisite": "A Coda account; the hosted MCP service is currently beta.", "riskTier": "S3" },
|
||||
{ "slug": "hugging-face", "name": "Hugging Face", "wave": 1, "status": "self_serve", "docsUrl": "https://huggingface.co/docs/hub/agents-mcp", "serverUrl": "https://huggingface.co/mcp?login&gradio=none", "authMode": "dcr_cimd", "prerequisite": "A Hugging Face account.", "riskTier": "S2" },
|
||||
{ "slug": "kernel", "name": "Kernel", "wave": 1, "status": "self_serve", "docsUrl": "https://www.kernel.sh/docs/reference/mcp-server/", "serverUrl": "https://mcp.onkernel.com/mcp", "authMode": "dcr_or_api_key", "prerequisite": "A Kernel account with either browser authorization or an API key.", "riskTier": "S3" },
|
||||
{ "slug": "local-falcon", "name": "Local Falcon", "wave": 1, "status": "self_serve", "docsUrl": "https://docs.localfalcon.com/", "serverUrl": "https://mcp.localfalcon.com", "authMode": "dcr", "prerequisite": "A Local Falcon account with MCP access.", "riskTier": "S2" },
|
||||
{ "slug": "make", "name": "Make", "wave": 1, "status": "self_serve", "docsUrl": "https://developers.make.com/mcp-server", "serverUrl": "https://mcp.make.com", "authMode": "dcr", "prerequisite": "A Make account and access to the scenarios exposed to MCP.", "riskTier": "S3" },
|
||||
{ "slug": "manufact", "name": "Manufact", "wave": 1, "status": "self_serve", "docsUrl": "https://docs.manufact.com/mcp", "serverUrl": "https://mcp.manufact.com/mcp", "authMode": "dcr", "prerequisite": "A Manufact account with MCP access.", "riskTier": "S3" },
|
||||
{ "slug": "miro", "name": "Miro", "wave": 1, "status": "self_serve", "docsUrl": "https://help.miro.com/hc/en-us/articles/31625301583890-How-to-enable-Miro-s-MCP-Server-user-guide", "serverUrl": "https://mcp.miro.com/", "authMode": "dcr", "prerequisite": "A Miro account; enterprise administrators may restrict third-party MCP clients.", "riskTier": "S3" },
|
||||
{ "slug": "netlify", "name": "Netlify", "wave": 1, "status": "self_serve", "docsUrl": "https://docs.netlify.com/build/build-with-ai/agent-setup-guides/agent-setup-overview/", "serverUrl": "https://netlify-mcp.netlify.app/mcp", "authMode": "dcr", "prerequisite": "A Netlify account with access to the relevant team and sites.", "riskTier": "S3" },
|
||||
{ "slug": "notion", "name": "Notion", "wave": 1, "status": "self_serve", "docsUrl": "https://developers.notion.com/guides/mcp/build-mcp-client", "serverUrl": "https://mcp.notion.com/mcp", "authMode": "dcr", "prerequisite": "A Notion account and access to the pages or databases being shared.", "riskTier": "S3" },
|
||||
{ "slug": "oreilly", "name": "O'Reilly", "wave": 1, "status": "self_serve", "docsUrl": "https://learning.oreilly.com/apidocs/mcp/content/", "serverUrl": "https://api.oreilly.com/api/content-discovery/v1/mcp/", "authMode": "dcr_or_api_key", "prerequisite": "An O'Reilly Learning subscription with MCP or API access.", "riskTier": "S2" },
|
||||
{ "slug": "planetscale", "name": "PlanetScale", "wave": 1, "status": "self_serve", "docsUrl": "https://planetscale.com/docs/connect/mcp", "serverUrl": "https://mcp.pscale.dev/mcp/planetscale", "authMode": "dcr", "prerequisite": "A PlanetScale account; database and branch access are chosen during authorization.", "riskTier": "S4" },
|
||||
{ "slug": "posthog", "name": "PostHog", "wave": 1, "status": "self_serve", "docsUrl": "https://posthog.com/docs/model-context-protocol", "serverUrl": "https://mcp.posthog.com/mcp", "authMode": "dcr_or_api_key", "prerequisite": "A PostHog account; personal API keys and optional organization or project pinning are supported as alternatives and advanced controls.", "riskTier": "S3" },
|
||||
{ "slug": "resend", "name": "Resend", "wave": 1, "status": "self_serve", "docsUrl": "https://resend.com/changelog/remote-mcp-server", "serverUrl": "https://mcp.resend.com/mcp", "authMode": "dcr", "prerequisite": "A Resend account with access to the relevant domains.", "riskTier": "S3" },
|
||||
{ "slug": "sentry", "name": "Sentry", "wave": 1, "status": "self_serve", "docsUrl": "https://mcp.sentry.dev/.well-known/oauth-authorization-server", "serverUrl": "https://mcp.sentry.dev/mcp", "authMode": "dcr_cimd", "prerequisite": "A Sentry account with access to the relevant organizations and projects.", "riskTier": "S3" },
|
||||
{ "slug": "ticktick", "name": "TickTick", "wave": 1, "status": "self_serve", "docsUrl": "https://help.ticktick.com/articles/7438129581631995904", "serverUrl": "https://mcp.ticktick.com", "authMode": "dcr", "prerequisite": "A TickTick account with MCP access.", "riskTier": "S3" },
|
||||
{ "slug": "todoist", "name": "Todoist", "wave": 1, "status": "self_serve", "docsUrl": "https://developer.todoist.com/", "serverUrl": "https://ai.todoist.net/mcp", "authMode": "dcr", "prerequisite": "A Todoist account.", "riskTier": "S3" },
|
||||
{ "slug": "webflow", "name": "Webflow", "wave": 1, "status": "self_serve", "docsUrl": "https://developers.webflow.com/mcp/reference/getting-started", "serverUrl": "https://mcp.webflow.com/mcp", "authMode": "dcr", "prerequisite": "A Webflow account; workspace and site roles constrain accessible sites.", "riskTier": "S3" },
|
||||
{ "slug": "wix", "name": "Wix", "wave": 1, "status": "self_serve", "docsUrl": "https://www.wix.com/studio/developers/mcp-server", "serverUrl": "https://mcp.wix.com/mcp", "authMode": "dcr", "prerequisite": "A Wix account with access to the relevant sites.", "riskTier": "S3" },
|
||||
|
||||
{ "slug": "brex", "name": "Brex", "wave": 2, "status": "self_serve", "docsUrl": "https://www.brex.com/support/using-brex-in-ai-apps", "serverUrl": "https://api.brex.com/mcp", "authMode": "dcr", "prerequisite": "Brex early access and an administrator enabling the integration; financial actions require explicit approval.", "riskTier": "S4" },
|
||||
{ "slug": "clickhouse", "name": "ClickHouse", "wave": 2, "status": "self_serve", "docsUrl": "https://clickhouse.com/blog/announcing-managed-clickstack-mcp-server", "serverUrl": "https://mcp.clickhouse.cloud/clickstack", "authMode": "dcr", "prerequisite": "A ClickHouse Cloud ClickStack service and its service ID.", "riskTier": "S4" },
|
||||
{ "slug": "egnyte", "name": "Egnyte", "wave": 2, "status": "self_serve", "docsUrl": "https://developers.egnyte.com/docs/Remote_MCP_Server", "serverUrl": "https://mcp-server.egnyte.com/mcp", "authMode": "dcr", "prerequisite": "An eligible Egnyte plan and administrator approval for external LLM access.", "riskTier": "S3" },
|
||||
{ "slug": "embat", "name": "Embat", "wave": 2, "status": "self_serve", "docsUrl": "https://tellme.embat.io/.well-known/oauth-protected-resource/mcp", "serverUrl": "https://tellme.embat.io/mcp", "authMode": "dcr_cimd", "prerequisite": "An Embat account; pilot the connection because provider setup documentation is sparse.", "riskTier": "S4" },
|
||||
{ "slug": "mixpanel", "name": "Mixpanel", "wave": 2, "status": "self_serve", "docsUrl": "https://mixpanel.com/blog/mixpanel-mcp-server/", "serverUrl": "https://mcp.mixpanel.com/mcp", "authMode": "dcr", "prerequisite": "A Mixpanel account; the hosted MCP server is currently beta.", "riskTier": "S3" },
|
||||
{ "slug": "postman", "name": "Postman", "wave": 2, "status": "self_serve", "docsUrl": "https://learning.postman.com/latest-v-12/docs/reference/postman-api/postman-mcp-server/postman-mcp-remote-server", "serverUrl": "https://mcp.postman.com/minimal", "authMode": "dcr_or_api_key", "prerequisite": "A Postman account; OAuth is available for US endpoints and API keys are required for EU endpoints.", "riskTier": "S3" },
|
||||
{ "slug": "razorpay", "name": "Razorpay", "wave": 2, "status": "self_serve", "docsUrl": "https://razorpay.com/docs/mcp-server/oauth/", "serverUrl": "https://mcp.razorpay.com/mcp", "authMode": "dcr_or_api_key", "prerequisite": "A Razorpay account; financial or destructive actions always require explicit approval.", "riskTier": "S4" },
|
||||
{ "slug": "sanity", "name": "Sanity", "wave": 2, "status": "self_serve", "docsUrl": "https://www.sanity.io/docs/ai/mcp-server", "serverUrl": "https://mcp.sanity.io", "authMode": "dcr_or_api_key", "prerequisite": "A Sanity account with access to the relevant projects and datasets.", "riskTier": "S3" },
|
||||
{ "slug": "stripe", "name": "Stripe", "wave": 2, "status": "self_serve", "docsUrl": "https://docs.stripe.com/mcp", "serverUrl": "https://mcp.stripe.com", "authMode": "dcr_or_api_key", "prerequisite": "A Stripe account; the server is public preview and payment actions require explicit approval.", "riskTier": "S4" },
|
||||
{ "slug": "supabase", "name": "Supabase", "wave": 2, "status": "self_serve", "docsUrl": "https://supabase.com/docs/guides/ai-tools/mcp", "serverUrl": "https://mcp.supabase.com/mcp", "authMode": "dcr_or_api_key", "prerequisite": "A Supabase account; use a development project and review write actions before connecting production data.", "riskTier": "S4" },
|
||||
{ "slug": "ticket-tailor", "name": "Ticket Tailor", "wave": 2, "status": "self_serve", "docsUrl": "https://developers.tickettailor.com/docs/mcp/authentication/", "serverUrl": "https://mcp.tickettailor.ai/mcp", "authMode": "dcr", "prerequisite": "A Ticket Tailor account; the provider may request an API key during its hosted authorization prompt.", "riskTier": "S3" },
|
||||
|
||||
{ "slug": "asana", "name": "Asana", "wave": 3, "status": "self_serve", "docsUrl": "https://developers.asana.com/docs/integrating-with-asanas-mcp-server", "serverUrl": "https://mcp.asana.com/v2/mcp", "authMode": "customer_oauth", "prerequisite": "Create an Asana MCP OAuth app and register Paperclip's callback URI; DCR is not supported.", "riskTier": "S3" },
|
||||
{ "slug": "box", "name": "Box", "wave": 3, "status": "self_serve", "docsUrl": "https://support.box.com/hc/en-us/articles/43847256139923-Managing-Box-MCP-Servers", "serverUrl": "https://mcp.box.com", "authMode": "customer_oauth", "prerequisite": "A Box administrator creates the OAuth integration and enables AI access.", "riskTier": "S3" },
|
||||
{ "slug": "mem0", "name": "Mem0", "wave": 3, "status": "self_serve", "docsUrl": "https://docs.mem0.ai/platform/mem0-mcp", "serverUrl": "https://mcp.mem0.ai/mcp/", "authMode": "api_key", "prerequisite": "A Mem0 API key; the live server currently requires the slash-normalized endpoint.", "riskTier": "S3" },
|
||||
{ "slug": "pagerduty", "name": "PagerDuty", "wave": 3, "status": "self_serve", "docsUrl": "https://support.pagerduty.com/main/docs/pagerduty-mcp-server", "serverUrl": "https://mcp.pagerduty.com/mcp", "authMode": "api_key", "prerequisite": "A PagerDuty API token; choose the regional endpoint that hosts the account.", "riskTier": "S4" },
|
||||
{ "slug": "similarweb", "name": "Similarweb", "wave": 3, "status": "self_serve", "docsUrl": "https://developers.similarweb.com/docs/similarweb-mcp", "serverUrl": "https://mcp.similarweb.com", "authMode": "api_key", "prerequisite": "A Similarweb subscription with API access and an API key.", "riskTier": "S2" },
|
||||
{ "slug": "xero", "name": "Xero", "wave": 3, "status": "self_serve", "docsUrl": "https://developer.xero.com/ai", "serverUrl": "https://mcp.xero.com/mcp", "authMode": "customer_oauth", "prerequisite": "Create a Xero OAuth app and confirm the applicable AI and data-use terms before connecting.", "riskTier": "S4" },
|
||||
{ "slug": "zapier", "name": "Zapier", "wave": 3, "status": "self_serve", "docsUrl": "https://docs.zapier.com/mcp/quickstart", "serverUrl": "https://mcp.zapier.com/", "authMode": "generated_url", "prerequisite": "Create a Zapier MCP server, choose the actions it exposes, and paste its generated connection URL.", "riskTier": "S3" },
|
||||
|
||||
{ "slug": "g2", "name": "G2", "wave": "blocked", "status": "blocked", "docsUrl": "https://documentation.g2.com/docs/g2-mcp-server", "serverUrl": "https://mcp.g2.com/mcp", "authMode": "provider_approval", "prerequisite": "G2 must enable cross-application token introspection before an independently registered client can work.", "riskTier": "S3" },
|
||||
{ "slug": "vercel", "name": "Vercel", "wave": "blocked", "status": "blocked", "docsUrl": "https://vercel.com/docs/agent-resources/vercel-mcp", "serverUrl": "https://mcp.vercel.com", "authMode": "provider_approval", "prerequisite": "Vercel currently reviews and approves MCP clients.", "riskTier": "S3" },
|
||||
{ "slug": "zomato", "name": "Zomato", "wave": "blocked", "status": "blocked", "docsUrl": "https://github.com/Zomato/mcp-server-manifest", "serverUrl": "https://mcp-server.zomato.com/mcp", "authMode": "provider_approval", "prerequisite": "Zomato currently limits third-party clients and requires redirect-URI allowlisting.", "riskTier": "S3" }
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import manifest from "./self-serve-mcp-research.json" with { type: "json" };
|
||||
import type { SelfServeMcpResearchManifest } from "./types/app-definition.js";
|
||||
|
||||
export const SELF_SERVE_MCP_RESEARCH = manifest as SelfServeMcpResearchManifest;
|
||||
export const SELF_SERVE_MCP_CANDIDATES = SELF_SERVE_MCP_RESEARCH.entries.filter(
|
||||
(entry) => entry.status === "self_serve",
|
||||
);
|
||||
export const BLOCKED_MCP_PROVIDERS = SELF_SERVE_MCP_RESEARCH.entries.filter(
|
||||
(entry) => entry.status === "blocked",
|
||||
);
|
||||
|
|
@ -1,6 +1,33 @@
|
|||
import type { ConnectionGrantKind, ToolConnectionOwnership, ToolConnectionTransport } from "./tool-access.js";
|
||||
import type { ConnectionGrantKind, ToolConnectionOwnership, ToolConnectionTransport, VercelConnectPrincipalMode } from "./tool-access.js";
|
||||
export type AppCategory = "ai"|"analytics"|"commerce"|"communication"|"content"|"data"|"developer"|"productivity"|"other";
|
||||
export type OAuthRedirectConstraints = "https-or-loopback-http";
|
||||
export interface FieldDef { key:string; label:string; type:"text"|"password"|"textarea"|"datetime"|"select"|"checkbox"; required?:boolean; advanced?:boolean; placeholder?:string; helperMd?:string; secret?:boolean; prefix?:string; defaultValue?:string|boolean; validation?:{pattern?:string;maxLength?:number}; options?:Array<{value:string;label:string}>; transport?:{location:"query"|"header";name:string;format?:"string"|"csv"|"boolean";omitFalse?:boolean} }
|
||||
export interface ConnectionMethodDef { key:string; label?:string; transport:ToolConnectionTransport; auth:"oauth"|"api_key"|"none"; oauthStrategy?:"paperclip_id_connector"; grantKinds?:ConnectionGrantKind[]; ownershipModes:ToolConnectionOwnership[]; whenToUse:string; defaults?:{serverUrl?:string;discoveryUrl?:string|null;serviceHost?:string;templateKey?:string;authorizationEndpoint?:string;tokenEndpoint?:string;metadataUrl?:string;scopesHint?:string[]}; tenantFields?:FieldDef[]; extensionFields?:FieldDef[]; configRequirements?:{atLeastOneOf?:string[]}; credentialFields?:FieldDef[]; keyPlacement?:{location:"header"|"query"|"body_json"|"env";name:string;prefix?:string|null}; guidanceMd:string; consoleLinks?:{register?:string;keys?:string;settings?:string;docs?:string}; warnings?:string[]; variants?:Array<{key:string;label:string;whenToUse:string;tenantFields?:FieldDef[]}>; riskTier:"S1"|"S2"|"S3"|"S4"; requiredResourceFilters?:string[] }
|
||||
export interface AppDefinition { schemaVersion:1; slug:string; name:string; description:string; categories:AppCategory[]; featured?:boolean; branding:{logoUrl:string;darkLogoUrl?:string;backgroundColor?:string;accentColor?:string}; urlPatterns:string[]; docsUrl?:string; redirectConstraints?:OAuthRedirectConstraints; methods:ConnectionMethodDef[]; suggestable?:boolean; availability?:{available:boolean;reason?:string;robotEmail?:string}; ownershipAvailability?:Partial<Record<ToolConnectionOwnership,boolean>> }
|
||||
export interface FieldDef { key:string; label:string; type:"text"|"password"|"textarea"|"datetime"|"select"|"checkbox"; required?:boolean; advanced?:boolean; hidden?:boolean; placeholder?:string; helperMd?:string; secret?:boolean; prefix?:string; defaultValue?:string|boolean; validation?:{pattern?:string;maxLength?:number}; options?:Array<{value:string;label:string}>; transport?:{location:"query"|"header";name:string;format?:"string"|"csv"|"boolean";omitFalse?:boolean} }
|
||||
export interface ConnectionMethodDef { key:string; label?:string; transport:ToolConnectionTransport; auth:"oauth"|"api_key"|"none"; oauthStrategy?:"paperclip_id_connector"; connectorProfile?:string; capabilityProfile?:{key:string;label:string;description?:string}; grantKinds?:ConnectionGrantKind[]; ownershipModes:ToolConnectionOwnership[]; whenToUse:string; defaults?:{serverUrl?:string;serverUrlTemplate?:string;discoveryUrl?:string|null;serviceHost?:string;templateKey?:string;authorizationEndpoint?:string;tokenEndpoint?:string;metadataUrl?:string;scopesHint?:string[];oauthAuthorizationParams?:{access_type?:"offline";prompt?:"consent"}}; tenantFields?:FieldDef[]; extensionFields?:FieldDef[]; configRequirements?:{atLeastOneOf?:string[]}; credentialFields?:FieldDef[]; keyPlacement?:{location:"header"|"query"|"body_json"|"env";name:string;prefix?:string|null}; credentialSources?:{vercelConnect?:{services:string[];principalModes:VercelConnectPrincipalMode[];scopes:string[];header:{name:string;prefix?:string|null}}}; guidanceMd:string; consoleLinks?:{register?:string;keys?:string;settings?:string;docs?:string}; warnings?:string[]; variants?:Array<{key:string;label:string;whenToUse:string;tenantFields?:FieldDef[]}>; riskTier:"S1"|"S2"|"S3"|"S4"; requiredResourceFilters?:string[] }
|
||||
export interface AppDefinition { schemaVersion:1; slug:string; name:string; description:string; categories:AppCategory[]; featured?:boolean; branding:{logoUrl:string;darkLogoUrl?:string;backgroundColor?:string;accentColor?:string}; urlPatterns:string[]; docsUrl?:string; setupPrerequisite?:{title:string;description:string;steps?:string[];actionLabel:string;actionUrl:string}; redirectConstraints?:OAuthRedirectConstraints; methods:ConnectionMethodDef[]; suggestable?:boolean; availability?:{available:boolean;reason?:string;robotEmail?:string}; ownershipAvailability?:Partial<Record<ToolConnectionOwnership,boolean>> }
|
||||
|
||||
export type SelfServeMcpAuthMode =
|
||||
| "dcr"
|
||||
| "dcr_cimd"
|
||||
| "dcr_or_api_key"
|
||||
| "customer_oauth"
|
||||
| "api_key"
|
||||
| "generated_url"
|
||||
| "provider_approval";
|
||||
|
||||
export interface SelfServeMcpResearchEntry {
|
||||
slug: string;
|
||||
name: string;
|
||||
wave: 1 | 2 | 3 | "blocked";
|
||||
status: "self_serve" | "blocked";
|
||||
docsUrl: string;
|
||||
serverUrl: string;
|
||||
authMode: SelfServeMcpAuthMode;
|
||||
prerequisite: string;
|
||||
riskTier: "S1" | "S2" | "S3" | "S4";
|
||||
}
|
||||
|
||||
export interface SelfServeMcpResearchManifest {
|
||||
schemaVersion: 1;
|
||||
verifiedAt: string;
|
||||
entries: SelfServeMcpResearchEntry[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ export type ToolActorType = "agent" | "user" | "system" | "plugin";
|
|||
export type ToolConnectionTransport = "mcp_remote" | "rest_api" | "local_stdio";
|
||||
export type ToolConnectionAuthKind = "oauth" | "api_key" | "none";
|
||||
export type ToolConnectionOwnership = "platform_shared" | "platform_provisioned" | "customer" | "dcr";
|
||||
export type VercelConnectPrincipalMode = "app" | "user";
|
||||
export type ToolConnectionStatus = "draft" | "active" | "disabled" | "archived";
|
||||
export type ToolConnectionInstallTargetType = "company" | "agent";
|
||||
export type ConnectionGrantKind = "organization" | "user";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import { z } from "zod";
|
||||
import { connectionGrantKindSchema, toolConnectionOwnershipSchema, toolConnectionTransportSchema } from "./tool-access.js";
|
||||
const field=z.object({key:z.string().min(1),label:z.string().min(1),type:z.enum(["text","password","textarea","datetime","select","checkbox"]),required:z.boolean().optional(),advanced:z.boolean().optional(),placeholder:z.string().optional(),helperMd:z.string().optional(),secret:z.boolean().optional(),prefix:z.string().optional(),defaultValue:z.union([z.string(),z.boolean()]).optional(),validation:z.object({pattern:z.string().optional(),maxLength:z.number().int().positive().optional()}).optional(),options:z.array(z.object({value:z.string(),label:z.string()})).optional(),transport:z.object({location:z.enum(["query","header"]),name:z.string().min(1),format:z.enum(["string","csv","boolean"]).optional(),omitFalse:z.boolean().optional()}).optional()}).superRefine((v,c)=>{if(v.required&&v.type!=="checkbox"&&!v.placeholder)c.addIssue({code:"custom",message:"Required fields need placeholders",path:["placeholder"]});if(v.type==="select"&&(!v.options||v.options.length===0))c.addIssue({code:"custom",message:"Select fields need options",path:["options"]})});
|
||||
export const connectionMethodDefSchema=z.object({key:z.string().min(1),label:z.string().min(1).optional(),transport:toolConnectionTransportSchema,auth:z.enum(["oauth","api_key","none"]),oauthStrategy:z.enum(["paperclip_id_connector"]).optional(),grantKinds:z.array(connectionGrantKindSchema).min(1).optional(),ownershipModes:z.array(toolConnectionOwnershipSchema).min(1),whenToUse:z.string().min(1),defaults:z.object({serverUrl:z.string().url().optional(),discoveryUrl:z.string().url().nullable().optional(),serviceHost:z.string().optional(),templateKey:z.string().optional(),authorizationEndpoint:z.string().url().optional(),tokenEndpoint:z.string().url().optional(),metadataUrl:z.string().url().optional(),scopesHint:z.array(z.string()).optional()}).optional(),tenantFields:z.array(field).optional(),extensionFields:z.array(field).optional(),configRequirements:z.object({atLeastOneOf:z.array(z.string().min(1)).min(1).optional()}).optional(),credentialFields:z.array(field).optional(),keyPlacement:z.object({location:z.enum(["header","query","body_json","env"]),name:z.string().min(1),prefix:z.string().nullable().optional()}).optional(),guidanceMd:z.string().min(1),consoleLinks:z.object({register:z.string().url().optional(),keys:z.string().url().optional(),settings:z.string().url().optional(),docs:z.string().url().optional()}).optional(),warnings:z.array(z.string()).optional(),variants:z.array(z.object({key:z.string(),label:z.string(),whenToUse:z.string(),tenantFields:z.array(field).optional()})).optional(),riskTier:z.enum(["S1","S2","S3","S4"]),requiredResourceFilters:z.array(z.string()).optional()}).superRefine((v,c)=>{if(v.auth==="api_key"&&!v.keyPlacement)c.addIssue({code:"custom",message:"API-key methods require keyPlacement",path:["keyPlacement"]});if(v.oauthStrategy&&v.auth!=="oauth")c.addIssue({code:"custom",message:"OAuth strategies require OAuth auth",path:["oauthStrategy"]});const keys=new Set([...(v.tenantFields??[]),...(v.extensionFields??[])].map((entry)=>entry.key));for(const key of v.configRequirements?.atLeastOneOf??[])if(!keys.has(key))c.addIssue({code:"custom",message:"Config requirement references an unknown field",path:["configRequirements","atLeastOneOf"]})});
|
||||
export const appDefinitionSchema=z.object({schemaVersion:z.literal(1),slug:z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/),name:z.string().min(1),description:z.string().min(1),categories:z.array(z.enum(["ai","analytics","commerce","communication","content","data","developer","productivity","other"])).min(1),featured:z.boolean().optional(),branding:z.object({logoUrl:z.string().url(),darkLogoUrl:z.string().url().optional(),backgroundColor:z.string().optional(),accentColor:z.string().optional()}),urlPatterns:z.array(z.string()),docsUrl:z.string().url().optional(),redirectConstraints:z.enum(["https-or-loopback-http"]).optional(),methods:z.array(connectionMethodDefSchema).min(1),suggestable:z.boolean().optional(),availability:z.object({available:z.boolean(),reason:z.string().optional(),robotEmail:z.string().optional()}).optional(),ownershipAvailability:z.object({platform_shared:z.boolean().optional(),platform_provisioned:z.boolean().optional(),customer:z.boolean().optional(),dcr:z.boolean().optional()}).optional()});
|
||||
const appBrandAssetUrlSchema=z.string().refine((value)=>{
|
||||
if(/^\/brands\/apps\/[a-z0-9][a-z0-9._-]*\.(?:svg|png)$/i.test(value))return true;
|
||||
try{return new URL(value).protocol==="https:";}catch{return false;}
|
||||
},{message:"Brand assets must be HTTPS URLs or local /brands/apps SVG/PNG paths"});
|
||||
const field=z.object({key:z.string().min(1),label:z.string().min(1),type:z.enum(["text","password","textarea","datetime","select","checkbox"]),required:z.boolean().optional(),advanced:z.boolean().optional(),hidden:z.boolean().optional(),placeholder:z.string().optional(),helperMd:z.string().optional(),secret:z.boolean().optional(),prefix:z.string().optional(),defaultValue:z.union([z.string(),z.boolean()]).optional(),validation:z.object({pattern:z.string().optional(),maxLength:z.number().int().positive().optional()}).optional(),options:z.array(z.object({value:z.string(),label:z.string()})).optional(),transport:z.object({location:z.enum(["query","header"]),name:z.string().min(1),format:z.enum(["string","csv","boolean"]).optional(),omitFalse:z.boolean().optional()}).optional()}).superRefine((v,c)=>{if(v.required&&v.type!=="checkbox"&&!v.placeholder)c.addIssue({code:"custom",message:"Required fields need placeholders",path:["placeholder"]});if(v.type==="select"&&(!v.options||v.options.length===0))c.addIssue({code:"custom",message:"Select fields need options",path:["options"]});if(v.hidden&&v.defaultValue===undefined)c.addIssue({code:"custom",message:"Hidden fields need defaults",path:["defaultValue"]})});
|
||||
export const connectionMethodDefSchema=z.object({key:z.string().min(1),label:z.string().min(1).optional(),transport:toolConnectionTransportSchema,auth:z.enum(["oauth","api_key","none"]),oauthStrategy:z.enum(["paperclip_id_connector"]).optional(),connectorProfile:z.string().regex(/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/).optional(),capabilityProfile:z.object({key:z.string().min(1),label:z.string().min(1),description:z.string().min(1).optional()}).optional(),grantKinds:z.array(connectionGrantKindSchema).min(1).optional(),ownershipModes:z.array(toolConnectionOwnershipSchema).min(1),whenToUse:z.string().min(1),defaults:z.object({serverUrl:z.string().url().optional(),serverUrlTemplate:z.string().regex(/^https:\/\//).optional(),discoveryUrl:z.string().url().nullable().optional(),serviceHost:z.string().optional(),templateKey:z.string().optional(),authorizationEndpoint:z.string().url().optional(),tokenEndpoint:z.string().url().optional(),metadataUrl:z.string().url().optional(),scopesHint:z.array(z.string()).optional(),oauthAuthorizationParams:z.object({access_type:z.literal("offline").optional(),prompt:z.literal("consent").optional()}).optional()}).optional(),tenantFields:z.array(field).optional(),extensionFields:z.array(field).optional(),configRequirements:z.object({atLeastOneOf:z.array(z.string().min(1)).min(1).optional()}).optional(),credentialFields:z.array(field).optional(),keyPlacement:z.object({location:z.enum(["header","query","body_json","env"]),name:z.string().min(1),prefix:z.string().nullable().optional()}).optional(),credentialSources:z.object({vercelConnect:z.object({services:z.array(z.string().min(1)).min(1),principalModes:z.array(z.enum(["app","user"])).min(1),scopes:z.array(z.string().min(1)).min(1),header:z.object({name:z.string().min(1),prefix:z.string().nullable().optional()})}).optional()}).optional(),guidanceMd:z.string().min(1),consoleLinks:z.object({register:z.string().url().optional(),keys:z.string().url().optional(),settings:z.string().url().optional(),docs:z.string().url().optional()}).optional(),warnings:z.array(z.string()).optional(),variants:z.array(z.object({key:z.string(),label:z.string(),whenToUse:z.string(),tenantFields:z.array(field).optional()})).optional(),riskTier:z.enum(["S1","S2","S3","S4"]),requiredResourceFilters:z.array(z.string()).optional()}).superRefine((v,c)=>{if(v.auth==="api_key"&&!v.keyPlacement)c.addIssue({code:"custom",message:"API-key methods require keyPlacement",path:["keyPlacement"]});if(v.oauthStrategy&&v.auth!=="oauth")c.addIssue({code:"custom",message:"OAuth strategies require OAuth auth",path:["oauthStrategy"]});if(v.oauthStrategy==="paperclip_id_connector"&&!v.connectorProfile)c.addIssue({code:"custom",message:"Paperclip ID connector methods require connectorProfile",path:["connectorProfile"]});if(v.connectorProfile&&v.oauthStrategy!=="paperclip_id_connector")c.addIssue({code:"custom",message:"connectorProfile requires the Paperclip ID OAuth strategy",path:["connectorProfile"]});if(v.credentialSources?.vercelConnect&&(v.transport!=="mcp_remote"||v.auth==="none"))c.addIssue({code:"custom",message:"Vercel Connect requires an authenticated remote MCP method",path:["credentialSources","vercelConnect"]});const keys=new Set([...(v.tenantFields??[]),...(v.extensionFields??[])].map((entry)=>entry.key));for(const key of v.configRequirements?.atLeastOneOf??[])if(!keys.has(key))c.addIssue({code:"custom",message:"Config requirement references an unknown field",path:["configRequirements","atLeastOneOf"]});if(v.defaults?.serverUrl&&v.defaults.serverUrlTemplate)c.addIssue({code:"custom",message:"Use either serverUrl or serverUrlTemplate",path:["defaults"]});for(const placeholder of v.defaults?.serverUrlTemplate?.matchAll(/\{([a-zA-Z0-9_-]+)\}/g)??[])if(!keys.has(placeholder[1]))c.addIssue({code:"custom",message:"Server URL template references an unknown field",path:["defaults","serverUrlTemplate"]})});
|
||||
export const appDefinitionSchema=z.object({schemaVersion:z.literal(1),slug:z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/),name:z.string().min(1),description:z.string().min(1),categories:z.array(z.enum(["ai","analytics","commerce","communication","content","data","developer","productivity","other"])).min(1),featured:z.boolean().optional(),branding:z.object({logoUrl:appBrandAssetUrlSchema,darkLogoUrl:appBrandAssetUrlSchema.optional(),backgroundColor:z.string().optional(),accentColor:z.string().optional()}),urlPatterns:z.array(z.string()),docsUrl:z.string().url().optional(),setupPrerequisite:z.object({title:z.string().min(1),description:z.string().min(1),steps:z.array(z.string().min(1)).min(1).optional(),actionLabel:z.string().min(1),actionUrl:z.string().url()} ).optional(),redirectConstraints:z.enum(["https-or-loopback-http"]).optional(),methods:z.array(connectionMethodDefSchema).min(1),suggestable:z.boolean().optional(),availability:z.object({available:z.boolean(),reason:z.string().optional(),robotEmail:z.string().optional()}).optional(),ownershipAvailability:z.object({platform_shared:z.boolean().optional(),platform_provisioned:z.boolean().optional(),customer:z.boolean().optional(),dcr:z.boolean().optional()}).optional()});
|
||||
export const appDefinitionsSchema=z.array(appDefinitionSchema).superRefine((v,c)=>{const s=new Set<string>();v.forEach((a,i)=>{if(s.has(a.slug))c.addIssue({code:"custom",message:"Duplicate slug",path:[i,"slug"]});s.add(a.slug)})});
|
||||
|
|
|
|||
|
|
@ -1,33 +1,149 @@
|
|||
import fs from "node:fs"; import path from "node:path";
|
||||
const root=process.cwd(); const corpus=process.env.PAPERCLIP_CONTENT_TEMPLATES??path.resolve(root,"../../../paperclip-content/research/connections/vercel/templates");
|
||||
const out=path.join(root,"packages/shared/src/app-definitions"); const favicon=d=>`https://www.google.com/s2/favicons?domain=${d}&sz=128`;
|
||||
const root=process.cwd(); const corpus=process.env.PAPERCLIP_CONTENT_TEMPLATES??path.resolve(root,"../../paperclip-content/research/connections/vercel/templates");
|
||||
const out=path.join(root,"packages/shared/src/app-definitions");
|
||||
const brandingManifest=JSON.parse(fs.readFileSync(path.join(root,"ui/public/brands/apps/manifest.json"),"utf8"));
|
||||
const brandingBySlug=new Map(brandingManifest.providers.map((entry)=>[entry.slug,entry]));
|
||||
const brandingFor=(slug)=>{
|
||||
const entry=brandingBySlug.get(slug);
|
||||
if(entry) return {logoUrl:entry.localAsset,...(entry.darkAsset?{darkLogoUrl:entry.darkAsset}:{})};
|
||||
if(slug==="oauth-generic"||slug==="api-key-generic") return {logoUrl:`/brands/apps/${slug}.svg`};
|
||||
throw new Error(`${slug}: missing local branding provenance`);
|
||||
};
|
||||
const field=(key,label,placeholder)=>({key,label,type:"password",required:true,placeholder,secret:true});
|
||||
const method=(key,transport,auth,defaults,riskTier,guidanceMd,extra={})=>({key,transport,auth,ownershipModes:auth==="oauth"?["customer","dcr"]:["customer"],whenToUse:transport==="mcp_remote"?"Use the provider-hosted connection for the quickest setup.":"Use credentials from your provider account.",defaults,guidanceMd,riskTier,...extra});
|
||||
const vercelConnect=(serviceOrServices,principalMode,scopes,header={name:"Authorization",prefix:"Bearer "})=>({credentialSources:{vercelConnect:{services:Array.isArray(serviceOrServices)?serviceOrServices:[serviceOrServices],principalModes:[principalMode],scopes,header}}});
|
||||
const posthogConfigFields=()=>[
|
||||
{key:"projectId",label:"Project ID",type:"text",required:true,placeholder:"12345",helperMd:"Find the numeric project ID in PostHog project settings.",validation:{pattern:"^[0-9]+$",maxLength:32},transport:{location:"header",name:"x-posthog-project-id"}},
|
||||
{key:"readOnly",label:"Read-only mode",type:"checkbox",defaultValue:false,helperMd:"Turn on to hide tools that can change PostHog data.",transport:{location:"query",name:"readonly",format:"boolean",omitFalse:true}},
|
||||
{key:"projectId",label:"Pin to project ID",type:"text",advanced:true,placeholder:"Optional numeric project ID",helperMd:"Optional. Pin this connection to one project and remove PostHog's project-switching tool.",validation:{pattern:"^[0-9]+$",maxLength:32},transport:{location:"header",name:"x-posthog-project-id"}},
|
||||
{key:"readOnly",label:"Read-only mode",type:"checkbox",advanced:true,defaultValue:false,helperMd:"Turn on to hide tools that can change PostHog data.",transport:{location:"query",name:"readonly",format:"boolean",omitFalse:true}},
|
||||
{key:"features",label:"Feature groups",type:"textarea",advanced:true,placeholder:"Optional comma-separated feature groups",helperMd:"Leave blank to expose every feature group, or enter a comma-separated list to narrow access.",validation:{maxLength:500},transport:{location:"query",name:"features",format:"csv"}},
|
||||
{key:"tools",label:"Individual tools",type:"textarea",advanced:true,placeholder:"Optional comma-separated tool names",helperMd:"Leave blank to expose all tools. Exact names here are combined with any feature groups.",validation:{maxLength:2000},transport:{location:"query",name:"tools",format:"csv"}},
|
||||
{key:"mode",label:"Tool response mode",type:"select",advanced:true,required:true,placeholder:"Individual tools",defaultValue:"tools",options:[{value:"tools",label:"Individual tools"}],helperMd:"Paperclip uses individual tools so every action can be governed. CLI mode remains unavailable until nested execution is governed.",transport:{location:"query",name:"mode"}},
|
||||
{key:"mode",label:"Tool response mode",type:"select",hidden:true,required:true,placeholder:"Individual tools",defaultValue:"tools",options:[{value:"tools",label:"Individual tools"}],helperMd:"Paperclip uses individual tools so every action can be governed. CLI mode remains unavailable until nested execution is governed.",transport:{location:"query",name:"mode"}},
|
||||
];
|
||||
const posthogMethod=(key,auth,extra={})=>method(key,"mcp_remote",auth,{serverUrl:"https://mcp.posthog.com/mcp"},"S3","Pin the connection to one PostHog project and expose the full tool catalog by default. Narrow feature groups or tools only when needed.",{tenantFields:posthogConfigFields(),requiredResourceFilters:["project"],...extra});
|
||||
const posthogMethod=(key,auth,extra={})=>method(key,"mcp_remote",auth,{serverUrl:"https://mcp.posthog.com/mcp"},"S3","Connect with PostHog's recommended defaults. Project pinning, read-only access, and catalog filters are optional advanced controls.",{tenantFields:posthogConfigFields(),...extra});
|
||||
const apps=[
|
||||
["zapier","Zapier","Reach thousands of apps through your Zapier account.","productivity","zapier.com",["https://mcp.zapier.com/*"],method("mcp-key","mcp_remote","api_key",{serverUrl:"https://mcp.zapier.com/api/mcp"},"S3","Create a Zapier MCP connection, then paste its token here.",{credentialFields:[field("authorization","Zapier MCP token","Paste your Zapier token")],keyPlacement:{location:"header",name:"Authorization",prefix:"Bearer "}})],
|
||||
["zapier","Zapier","Reach thousands of apps through your Zapier account.","productivity","zapier.com",["https://mcp.zapier.com/*"],method("generated-url","mcp_remote","none",{},"S3","Create a Zapier MCP server, then paste the complete generated connection URL. The token remains embedded in that URL.",{label:"Paste generated MCP URL",whenToUse:"Use the complete provider-generated MCP URL from Zapier."})],
|
||||
["github","GitHub","Read code and pull requests, and coordinate repository work.","developer","github.com",["https://api.githubcopilot.com/mcp/*"],method("mcp-key","mcp_remote","api_key",{serverUrl:"https://api.githubcopilot.com/mcp/"},"S3","Create a fine-grained token limited to the repositories agents should use.",{credentialFields:[field("authorization","GitHub token","github_pat_...")],keyPlacement:{location:"header",name:"Authorization",prefix:"Bearer "},requiredResourceFilters:["organization","repository"]})],
|
||||
["slack","Slack","Search channels and coordinate team communication.","communication","slack.com",["https://mcp.slack.com/*"],method("mcp-oauth","mcp_remote","oauth",{serverUrl:"https://mcp.slack.com/mcp",authorizationEndpoint:"https://slack.com/oauth/v2/authorize",tokenEndpoint:"https://slack.com/api/oauth.v2.access",scopesHint:["channels:read","chat:write","search:read"]},"S3","Connect a Slack workspace and limit access to the channels agents need.",{requiredResourceFilters:["workspace","channel"]})],
|
||||
["notion","Notion","Read and update pages in your Notion workspace.","content","notion.so",["https://mcp.notion.com/*"],method("mcp-oauth","mcp_remote","oauth",{serverUrl:"https://mcp.notion.com/mcp"},"S3","Connect Notion for workspace content. Share only the pages and databases agents should use.",{requiredResourceFilters:["workspace","page","database"]}),{redirectConstraints:"https-or-loopback-http"}],
|
||||
["posthog","PostHog","Analyze product usage, errors, feature flags, and experiments in a pinned PostHog project.","analytics","posthog.com",["https://mcp.posthog.com/*"],[posthogMethod("mcp-oauth","oauth",{label:"Sign in with PostHog",ownershipModes:["customer","dcr"],whenToUse:"Sign in with PostHog in the browser. Recommended for hosted PostHog accounts.",consoleLinks:{docs:"https://posthog.com/docs/model-context-protocol"}}),posthogMethod("mcp-api-key","api_key",{label:"Use a personal API key",whenToUse:"Use a PostHog personal API key when browser sign-in is not suitable.",credentialFields:[field("authorization","PostHog personal API key","phx_...")],keyPlacement:{location:"header",name:"Authorization",prefix:"Bearer "},consoleLinks:{keys:"https://posthog.com/docs/model-context-protocol/faq",docs:"https://posthog.com/docs/model-context-protocol/faq"}})],{featured:true}],
|
||||
["linear","Linear","Create, update, and read Linear issues.","productivity","linear.app",["https://mcp.linear.app/*"],method("mcp-oauth","mcp_remote","oauth",{serverUrl:"https://mcp.linear.app/mcp",authorizationEndpoint:"https://linear.app/oauth/authorize",tokenEndpoint:"https://api.linear.app/oauth/token",scopesHint:["read","write"]},"S2","Register a Linear OAuth app and add Paperclip's redirect URI before connecting.",{requiredResourceFilters:["workspace","team","project"]})],
|
||||
["slack","Slack","Search channels and coordinate team communication.","communication","slack.com",["https://mcp.slack.com/*"],method("mcp-oauth","mcp_remote","oauth",{serverUrl:"https://mcp.slack.com/mcp",authorizationEndpoint:"https://slack.com/oauth/v2/authorize",tokenEndpoint:"https://slack.com/api/oauth.v2.access",scopesHint:["channels:read","chat:write","search:read"]},"S3","Connect a Slack workspace and limit access to the channels agents need.",{ownershipModes:["customer"],requiredResourceFilters:["workspace","channel"]})],
|
||||
["notion","Notion","Read and update pages in your Notion workspace.","content","notion.so",["https://mcp.notion.com/*"],method("mcp-oauth","mcp_remote","oauth",{serverUrl:"https://mcp.notion.com/mcp"},"S3","Connect Notion for workspace content. Share only the pages and databases agents should use.",{requiredResourceFilters:["workspace","page","database"],...vercelConnect("notion","user",["*"])}),{redirectConstraints:"https-or-loopback-http"}],
|
||||
["posthog","PostHog","Analyze product usage, errors, feature flags, and experiments with PostHog's hosted MCP server.","analytics","posthog.com",["https://mcp.posthog.com/*"],[posthogMethod("mcp-oauth","oauth",{label:"Sign in with PostHog",ownershipModes:["customer","dcr"],whenToUse:"Sign in with PostHog in the browser. Recommended for hosted PostHog accounts.",consoleLinks:{docs:"https://posthog.com/docs/model-context-protocol"},...vercelConnect(["posthog","mcp.posthog.com/mcp"],"user",["*"])}),posthogMethod("mcp-api-key","api_key",{label:"Use a personal API key",whenToUse:"Use a PostHog personal API key when browser sign-in is not suitable.",credentialFields:[field("authorization","PostHog personal API key","phx_...")],keyPlacement:{location:"header",name:"Authorization",prefix:"Bearer "},consoleLinks:{keys:"https://posthog.com/docs/model-context-protocol/faq",docs:"https://posthog.com/docs/model-context-protocol/faq"},...vercelConnect(["posthog","mcp.posthog.com/mcp"],"app",["*"])})],{featured:true}],
|
||||
["linear","Linear","Create, update, and read Linear issues.","productivity","linear.app",["https://mcp.linear.app/*"],method("mcp-oauth","mcp_remote","oauth",{serverUrl:"https://mcp.linear.app/mcp",authorizationEndpoint:"https://linear.app/oauth/authorize",tokenEndpoint:"https://api.linear.app/oauth/token",scopesHint:["read","write"]},"S2","Register a Linear OAuth app and add Paperclip's redirect URI before connecting.",{ownershipModes:["customer"],requiredResourceFilters:["workspace","team","project"],...vercelConnect("linear","user",["read","write"])})],
|
||||
["google-sheets","Google Sheets","Read and update selected spreadsheets.","data","sheets.google.com",["https://docs.google.com/spreadsheets/*","https://sheets.google.com/*"],method("local","local_stdio","none",{templateKey:"paperclip.google-sheets"},"S3","Share each spreadsheet with the Paperclip robot email, then paste the sheet links.",{requiredResourceFilters:["spreadsheet"]})],
|
||||
["context7","Context7","Look up current documentation for software libraries.","developer","context7.com",["https://mcp.context7.com/*"],method("mcp","mcp_remote","none",{serverUrl:"https://mcp.context7.com/mcp"},"S1","Connect Context7 to give agents current library documentation.")],
|
||||
["shopify","Shopify","Search a store's products and policies, and manage shopping carts.","commerce","shopify.com",["https://*.myshopify.com/api/mcp"],method("storefront-mcp","mcp_remote","none",{serverUrlTemplate:"https://{storeDomain}/api/mcp"},"S3","Connect Shopify's official Storefront MCP server for shopper-facing catalog, policy, and cart tools.",{label:"Connect a Shopify storefront",whenToUse:"Use a store's public myshopify.com domain. No Shopify app or OAuth registration is required.",tenantFields:[{key:"storeDomain",label:"Store domain",type:"text",required:true,placeholder:"your-store.myshopify.com",helperMd:"Enter the full myshopify.com domain without https://.",validation:{pattern:"^[A-Za-z0-9][A-Za-z0-9-]*\\.myshopify\\.com$",maxLength:255}}],consoleLinks:{docs:"https://shopify.dev/docs/apps/build/storefront-mcp/servers/storefront"},warnings:["This is Shopify's Storefront MCP, not Admin API access. It does not manage merchant products, orders, or customers.","The storefront must be publicly reachable. Password-protected or restricted trial stores can return HTTP 401."],requiredResourceFilters:["store"]})],
|
||||
["composio","Composio","Connect Composio so Paperclip can discover and manage the toolkits in your project.","productivity","composio.dev",["https://backend.composio.dev/*"],method("api-key","rest_api","api_key",{serviceHost:"backend.composio.dev"},"S3","Create a scoped project API key in Composio. It needs read access to toolkits and auth configs; later service-connection phases also need connected-account and session access.",{whenToUse:"Use a project API key from the Composio project that owns the toolkits and connected accounts.",credentialFields:[field("apiKey","Composio project API key","Paste the Composio API key")],keyPlacement:{location:"header",name:"x-api-key"},consoleLinks:{keys:"https://app.composio.dev/",settings:"https://app.composio.dev/",docs:"https://docs.composio.dev/reference/authenticating-to-composio/project-api-key-permissions"}}),{featured:true}],
|
||||
["oauth-generic","OAuth app","Connect a provider using your own OAuth client.","other","oauth.net",[],method("oauth","rest_api","oauth",{},"S3","Register an OAuth client with the provider and add Paperclip's redirect URI.",{credentialFields:[{...field("clientId","Client ID","Paste the client ID"),type:"text",secret:false},field("clientSecret","Client secret","Paste the client secret")]})],
|
||||
["api-key-generic","API key app","Connect an API using a key from your provider.","other","openapis.org",[],method("api-key","rest_api","api_key",{},"S3","Create a restricted API key and paste it here.",{credentialFields:[field("apiKey","API key","Paste the API key")],keyPlacement:{location:"header",name:"Authorization",prefix:"Bearer "}})],
|
||||
["sentry","Sentry","Investigate errors, releases, and production issues.","developer","sentry.io",["https://mcp.sentry.dev/*"],method("mcp-oauth","mcp_remote","oauth",{serverUrl:"https://mcp.sentry.dev/mcp",discoveryUrl:"https://sentry.io/.well-known/oauth-authorization-server"},"S2","Connect the Sentry organization and projects agents need for incident work.",{requiredResourceFilters:["organization","project","environment"]})],
|
||||
["vercel","Vercel","Inspect projects, deployments, and runtime logs.","developer","vercel.com",["https://mcp.vercel.com/*"],method("mcp-oauth","mcp_remote","oauth",{serverUrl:"https://mcp.vercel.com/mcp"},"S3","Connect the Vercel team and projects agents should operate.",{requiredResourceFilters:["team","project","environment"]})],
|
||||
["anthropic","Anthropic","Use Anthropic APIs with a restricted key.","ai","anthropic.com",["https://api.anthropic.com/*"],method("api-key","rest_api","api_key",{serviceHost:"api.anthropic.com"},"S3","Create a key in the Anthropic Console and rotate it if it has been exposed.",{credentialFields:[field("apiKey","API key","sk-ant-api03-...")],keyPlacement:{location:"header",name:"x-api-key"}})],
|
||||
].map(([slug,name,description,category,domain,urlPatterns,m,extra={}])=>({schemaVersion:1,slug,name,description,categories:[category],featured:["zapier","github","slack","notion","posthog","linear"].includes(slug),branding:{logoUrl:favicon(domain)},urlPatterns,methods:Array.isArray(m)?m:[m],...extra}));
|
||||
apps.push({schemaVersion:1,slug:"gmail",name:"Gmail",description:"Search and read Gmail messages and create drafts without enabling mail sending.",categories:["communication","productivity"],featured:true,branding:{logoUrl:favicon("gmail.com")},urlPatterns:["https://gmailmcp.googleapis.com/*"],docsUrl:"https://developers.google.com/workspace/guides/configure-mcp-servers",redirectConstraints:"https-or-loopback-http",methods:[{key:"paperclip-id-oauth",label:"Connect Gmail",transport:"mcp_remote",auth:"oauth",oauthStrategy:"paperclip_id_connector",grantKinds:["user"],ownershipModes:["customer"],whenToUse:"Use Paperclip ID for a personal Gmail connection with centrally registered Google OAuth.",defaults:{serverUrl:"https://gmailmcp.googleapis.com/mcp/v1",scopesHint:["https://www.googleapis.com/auth/gmail.readonly","https://www.googleapis.com/auth/gmail.compose"]},guidanceMd:"Connect your Gmail identity. Paperclip can search and read mail and create drafts. Sending mail is not enabled.",warnings:["This connection is personal. Agents need an explicit install, profile, and delegation before they can use it."],riskTier:"S3"}]});
|
||||
].map(([slug,name,description,category,_domain,urlPatterns,m,extra={}])=>({schemaVersion:1,slug,name,description,categories:[category],featured:["zapier","github","slack","notion","posthog","linear"].includes(slug),branding:brandingFor(slug),urlPatterns,methods:Array.isArray(m)?m:[m],...extra}));
|
||||
apps.push({schemaVersion:1,slug:"gmail",name:"Gmail",description:"Search and read Gmail messages and create drafts without enabling mail sending.",categories:["communication","productivity"],featured:true,branding:brandingFor("gmail"),urlPatterns:["https://gmailmcp.googleapis.com/*"],docsUrl:"https://developers.google.com/workspace/guides/configure-mcp-servers",redirectConstraints:"https-or-loopback-http",methods:[{key:"paperclip-id-oauth",label:"Connect Gmail",transport:"mcp_remote",auth:"oauth",oauthStrategy:"paperclip_id_connector",grantKinds:["user"],ownershipModes:["customer"],whenToUse:"Use Paperclip ID for a personal Gmail connection with centrally registered Google OAuth.",defaults:{serverUrl:"https://gmailmcp.googleapis.com/mcp/v1",scopesHint:["https://www.googleapis.com/auth/gmail.readonly","https://www.googleapis.com/auth/gmail.compose"]},guidanceMd:"Connect your Gmail identity. Paperclip can search and read mail and create drafts. Sending mail is not enabled.",warnings:["This connection is personal. Agents need an explicit install, profile, and delegation before they can use it."],riskTier:"S3"}]});
|
||||
|
||||
// The reviewed MCP program is a durable input, not another hand-maintained
|
||||
// allowlist. Runtime definitions are generated from the same 46-row evidence
|
||||
// ledger that the tests and implementation checklist validate.
|
||||
const researchManifest=JSON.parse(fs.readFileSync(path.join(root,"packages/shared/src/self-serve-mcp-research.json"),"utf8"));
|
||||
const categoryBySlug={
|
||||
airtable:"data",asana:"productivity",beehiiv:"content",bitly:"analytics",box:"content",brex:"commerce",candid:"data",clickhouse:"data",cloudflare:"developer",cloudinary:"content",coda:"productivity",egnyte:"content",embat:"commerce","hugging-face":"ai",jira:"productivity",kernel:"developer","local-falcon":"analytics",make:"productivity",manufact:"productivity",mem0:"ai",miro:"productivity",mixpanel:"analytics",netlify:"developer",notion:"content",oreilly:"content",pagerduty:"developer",planetscale:"data",posthog:"analytics",postman:"developer",razorpay:"commerce",resend:"communication",sanity:"content",sentry:"developer",similarweb:"analytics",stripe:"commerce",supabase:"data","ticket-tailor":"commerce",ticktick:"productivity",todoist:"productivity",webflow:"content",wix:"content",xero:"commerce",zapier:"productivity",
|
||||
};
|
||||
const oauthMethodFor=(entry,key="mcp-oauth",serverUrl=entry.serverUrl,extra={})=>method(key,"mcp_remote","oauth",{serverUrl},entry.riskTier,`Connect ${entry.name} in the browser. ${entry.prerequisite}`,{label:`Sign in with ${entry.name}`,ownershipModes:["dcr"],whenToUse:"Use browser sign-in for the provider-hosted MCP server.",consoleLinks:{docs:entry.docsUrl},warnings:[entry.prerequisite],...extra});
|
||||
const customerOAuthMethodFor=(entry)=>oauthMethodFor(entry,"mcp-own-oauth",entry.serverUrl,{label:"Use your own OAuth app",ownershipModes:["customer"],whenToUse:`Register an OAuth app with ${entry.name}, then enter its client ID and secret.`,consoleLinks:{register:entry.docsUrl,docs:entry.docsUrl}});
|
||||
const apiKeySpec={
|
||||
bitly:{name:"Authorization",prefix:"Bearer ",placeholder:"Paste your Bitly API token"},
|
||||
cloudflare:{name:"Authorization",prefix:"Bearer ",placeholder:"Paste your Cloudflare API token"},
|
||||
coda:{name:"Authorization",prefix:"Bearer ",placeholder:"Paste your Coda API token"},
|
||||
kernel:{name:"X-API-Key",prefix:null,placeholder:"Paste your Kernel API key"},
|
||||
mem0:{name:"Authorization",prefix:"Bearer ",placeholder:"m0sk_..."},
|
||||
oreilly:{name:"Authorization",prefix:"Bearer ",placeholder:"Paste your O'Reilly API token"},
|
||||
pagerduty:{name:"Authorization",prefix:"Token token=",placeholder:"Paste your PagerDuty user API token"},
|
||||
postman:{name:"X-API-Key",prefix:null,placeholder:"PMAK-..."},
|
||||
razorpay:{name:"Authorization",prefix:"Basic ",placeholder:"Paste the base64-encoded key ID and secret"},
|
||||
sanity:{name:"Authorization",prefix:"Bearer ",placeholder:"sk..."},
|
||||
similarweb:{name:"api-key",prefix:null,placeholder:"Paste your Similarweb API key"},
|
||||
stripe:{name:"Authorization",prefix:"Bearer ",placeholder:"sk_..."},
|
||||
supabase:{name:"Authorization",prefix:"Bearer ",placeholder:"sbp_..."},
|
||||
};
|
||||
const apiKeyMethodFor=(entry,key="mcp-api-key",serverUrl=entry.serverUrl,extra={})=>{
|
||||
const spec=apiKeySpec[entry.slug]??{name:"Authorization",prefix:"Bearer ",placeholder:`Paste your ${entry.name} API key`};
|
||||
return method(key,"mcp_remote","api_key",{serverUrl},entry.riskTier,`Use a customer-created ${entry.name} key. ${entry.prerequisite}`,{label:"Use an API key",whenToUse:"Use a restricted customer-owned key when browser sign-in is not suitable.",credentialFields:[field("authorization",`${entry.name} API key`,spec.placeholder)],keyPlacement:{location:"header",name:spec.name,prefix:spec.prefix},consoleLinks:{keys:entry.docsUrl,docs:entry.docsUrl},warnings:[entry.prerequisite],...extra});
|
||||
};
|
||||
const specialMethodsFor=(entry)=>{
|
||||
// Atlassian's /authv2 rollout only issues GA-tool-compatible tokens when the
|
||||
// authorization request includes this reviewed protected-resource scope set.
|
||||
// Omitting scope currently yields agent-interface scopes that its own Jira
|
||||
// tools reject with HTTP 401. Users can still deselect write toolsets in the
|
||||
// provider consent screen; never replace this allowlist with live discovery.
|
||||
if(entry.slug==="jira") return [oauthMethodFor(entry,"mcp-oauth",entry.serverUrl,{defaults:{serverUrl:entry.serverUrl,scopesHint:["read:me","read:account","offline_access","email","read:jira-work","write:jira-work","search:confluence","read:confluence-user","read:page:confluence","write:page:confluence","read:comment:confluence","write:comment:confluence","read:space:confluence","read:hierarchical-content:confluence","write:component:compass","read:component:compass","read:scorecard:compass","write:scorecard:compass","read:event:compass","read:metric:compass","read:all:twg","write:all:twg"]}})];
|
||||
if(entry.slug==="hugging-face") return [oauthMethodFor(entry,"mcp-oauth",entry.serverUrl,{defaults:{serverUrl:entry.serverUrl,scopesHint:["read-mcp"]}})];
|
||||
if(entry.slug==="xero") return [oauthMethodFor(entry,"mcp-own-oauth",entry.serverUrl,{label:"Use your own OAuth app",ownershipModes:["customer"],whenToUse:`Register an OAuth app with ${entry.name}, then enter its client ID and secret.`,consoleLinks:{register:entry.docsUrl,docs:entry.docsUrl},defaults:{serverUrl:entry.serverUrl,scopesHint:["openid","profile","email","offline_access","accounting.settings","accounting.invoices.read","accounting.reports.aged.read","accounting.reports.balancesheet.read","accounting.reports.profitandloss.read"]}})];
|
||||
if(entry.slug==="clickhouse") return [oauthMethodFor(entry,"mcp-oauth",entry.serverUrl,{tenantFields:[{key:"serviceId",label:"ClickHouse Cloud service ID",type:"text",required:true,placeholder:"11e1031f-9a13-4cac-9bc7-d4ec9286ec17",helperMd:"Copy the service ID from ClickStack → Team Settings → API & Agents.",transport:{location:"header",name:"x-service-id"}}],requiredResourceFilters:["service"]})];
|
||||
if(entry.slug==="planetscale") return [
|
||||
oauthMethodFor(entry,"mcp-oauth",entry.serverUrl,{label:"Database access",tenantFields:[{key:"project",label:"Project or database",type:"text",advanced:true,placeholder:"Optional project or database name",helperMd:"Records the intended database boundary; final access is selected during PlanetScale authorization."},{key:"branch",label:"Branch",type:"text",advanced:true,placeholder:"Optional branch name",helperMd:"Records the intended branch boundary; final access is selected during PlanetScale authorization."}],requiredResourceFilters:["organization","database","branch"]}),
|
||||
oauthMethodFor(entry,"mcp-insights-only","https://mcp.pscale.dev/mcp/planetscale-insights-only",{label:"Insights only",whenToUse:"Use query insights and schema recommendations without query execution tools.",requiredResourceFilters:["organization","database","branch"]}),
|
||||
];
|
||||
if(entry.slug==="postman") return [
|
||||
oauthMethodFor(entry,"mcp-oauth-minimal","https://mcp.postman.com/minimal",{label:"US · Minimal"}),
|
||||
oauthMethodFor(entry,"mcp-oauth-code","https://mcp.postman.com/code",{label:"US · Code"}),
|
||||
oauthMethodFor(entry,"mcp-oauth-full","https://mcp.postman.com/mcp",{label:"US · Full"}),
|
||||
apiKeyMethodFor(entry,"mcp-eu-key-minimal","https://mcp.eu.postman.com/minimal",{label:"EU · Minimal"}),
|
||||
apiKeyMethodFor(entry,"mcp-eu-key-code","https://mcp.eu.postman.com/code",{label:"EU · Code"}),
|
||||
apiKeyMethodFor(entry,"mcp-eu-key-full","https://mcp.eu.postman.com/mcp",{label:"EU · Full"}),
|
||||
];
|
||||
if(entry.slug==="pagerduty") return [
|
||||
apiKeyMethodFor(entry,"mcp-api-key-us","https://mcp.pagerduty.com/mcp",{label:"US service region"}),
|
||||
apiKeyMethodFor(entry,"mcp-api-key-eu","https://mcp.eu.pagerduty.com/mcp",{label:"EU service region"}),
|
||||
];
|
||||
if(entry.slug==="supabase") {
|
||||
const tenantFields=[
|
||||
{key:"projectRef",label:"Project reference",type:"text",required:true,placeholder:"abcdefghijklmnopqrst",helperMd:"Scope the connection to one development project.",transport:{location:"query",name:"project_ref"}},
|
||||
{key:"readOnly",label:"Read-only mode",type:"checkbox",defaultValue:false,helperMd:"Enable this to prevent the connection from changing the database.",transport:{location:"query",name:"read_only",format:"boolean"}},
|
||||
{key:"features",label:"Feature groups",type:"textarea",advanced:true,placeholder:"database,docs",helperMd:"Optional comma-separated feature groups.",transport:{location:"query",name:"features",format:"csv"}},
|
||||
];
|
||||
const warning="Do not connect production data unless you have reviewed Supabase's MCP security guidance.";
|
||||
return [
|
||||
oauthMethodFor(entry,"mcp-oauth",entry.serverUrl,{guidanceMd:"Connect Supabase in the browser and scope the connection to one development project. Write tools start enabled and remain governed by Paperclip's action policies.",tenantFields,warnings:[entry.prerequisite,warning],requiredResourceFilters:["project"]}),
|
||||
apiKeyMethodFor(entry,"mcp-api-key",entry.serverUrl,{guidanceMd:"Use a customer-created Supabase key scoped to one development project. Write tools start enabled and remain governed by Paperclip's action policies.",tenantFields,warnings:[entry.prerequisite,warning],requiredResourceFilters:["project"]}),
|
||||
];
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
for(const entry of researchManifest.entries){
|
||||
const existing=apps.find((app)=>app.slug===entry.slug);
|
||||
if(entry.status==="blocked"){
|
||||
if(existing) existing.availability={available:false,reason:entry.prerequisite};
|
||||
continue;
|
||||
}
|
||||
if(existing){
|
||||
existing.docsUrl=entry.docsUrl;
|
||||
existing.redirectConstraints=existing.methods.some((entryMethod)=>entryMethod.auth==="oauth")?"https-or-loopback-http":existing.redirectConstraints;
|
||||
if(entry.slug!=="zapier") for(const entryMethod of existing.methods) if(entryMethod.transport==="mcp_remote"&&entryMethod.defaults?.serverUrl) entryMethod.defaults.serverUrl=entry.serverUrl;
|
||||
continue;
|
||||
}
|
||||
let methods=specialMethodsFor(entry);
|
||||
if(!methods){
|
||||
if(entry.authMode==="customer_oauth") methods=[customerOAuthMethodFor(entry)];
|
||||
else if(entry.authMode==="api_key") methods=[apiKeyMethodFor(entry)];
|
||||
else {
|
||||
methods=[oauthMethodFor(entry)];
|
||||
if(entry.authMode==="dcr_or_api_key") methods.push(apiKeyMethodFor(entry));
|
||||
}
|
||||
}
|
||||
const warnings=[];
|
||||
if(["coda","mixpanel"].includes(entry.slug)) warnings.push("This provider's hosted MCP server is currently beta or preview.");
|
||||
if(["brex","razorpay","stripe"].includes(entry.slug)) warnings.push("Financial or destructive actions must be explicitly approved before execution.");
|
||||
apps.push({schemaVersion:1,slug:entry.slug,name:entry.name,description:`Connect ${entry.name}'s provider-hosted MCP server.`,categories:[categoryBySlug[entry.slug]??"other"],featured:entry.slug==="jira",branding:brandingFor(entry.slug),urlPatterns:[`${new URL(entry.serverUrl).origin}/*`],docsUrl:entry.docsUrl,redirectConstraints:methods.some((entryMethod)=>entryMethod.auth==="oauth")?"https-or-loopback-http":undefined,methods:methods.map((entryMethod)=>warnings.length>0?{...entryMethod,warnings:[...(entryMethod.warnings??[]),...warnings]}:entryMethod)});
|
||||
}
|
||||
// Google Workspace definitions are reviewed, first-class app entries rather
|
||||
// than rows synthesized from the generic connection corpus. Keep each product
|
||||
// independent in the generated manifest while sharing only backend OAuth
|
||||
// infrastructure.
|
||||
const reviewedGoogleSlugs=["gmail","google-drive","google-docs","google-sheets","google-slides","google-calendar","google-chat","google-people","google-workspace-search"];
|
||||
for(const slug of reviewedGoogleSlugs){
|
||||
const existingIndex=apps.findIndex((app)=>app.slug===slug);
|
||||
if(existingIndex>=0) apps.splice(existingIndex,1);
|
||||
apps.push(JSON.parse(fs.readFileSync(path.join(out,`${slug}.json`),"utf8")));
|
||||
}
|
||||
const parseTableRow=(line)=>line.slice(1,-1).split("|").map((cell)=>cell.trim());
|
||||
const parseCapture=(fileName)=>{
|
||||
const markdown=fs.readFileSync(path.join(corpus,fileName),"utf8");
|
||||
|
|
@ -49,10 +165,14 @@ const inferState=(slug,state)=>{
|
|||
const transport=slug==="oauth-generic"||slug==="api-key-generic"||label.includes("path: api")||label.includes("api key form")?"rest_api":"mcp_remote";
|
||||
const auth=slug==="oauth-generic"||label.includes("oauth")||fieldText.includes("client id")?"oauth":slug==="api-key-generic"||label.includes("api key")||fieldText.includes("api key")?"api_key":null;
|
||||
const ownershipModes=[];
|
||||
if(label.includes("managed")) ownershipModes.push("platform_shared");
|
||||
// A "Managed" state in Vercel describes credential custody, not ownership of
|
||||
// a Paperclip connection. Keep those concepts separate: importing this review
|
||||
// evidence must never silently turn an operator-owned connector into
|
||||
// `platform_shared`.
|
||||
const externalCredentialCustody=label.includes("managed")&&!label.includes("no managed")?"vercel_connect":null;
|
||||
if(label.includes("your own credentials")||label.includes("manual")||label.includes("api key")) ownershipModes.push("customer");
|
||||
if(slug==="oauth-generic"&&!label.includes("manually")) ownershipModes.push("dcr");
|
||||
return {label:state.label,transport,auth,ownershipModes:[...new Set(ownershipModes)],fieldCount:state.fields.length,linkCount:state.links.length};
|
||||
return {label:state.label,transport,auth,ownershipModes:[...new Set(ownershipModes)],externalCredentialCustody,fieldCount:state.fields.length,linkCount:state.links.length};
|
||||
};
|
||||
const validateApp=(app)=>{
|
||||
if(app.schemaVersion!==1||!app.slug||!app.name||!Array.isArray(app.methods)||app.methods.length===0) throw new Error(`${app.slug||"unknown"}: invalid AppDefinition`);
|
||||
|
|
|
|||
|
|
@ -3403,23 +3403,32 @@ describeEmbeddedPostgres("tool access service", () => {
|
|||
canSetCompanyInstall: true,
|
||||
companyInstallReason: null,
|
||||
});
|
||||
expect(res.body.apps.map((app: { slug: string }) => app.slug)).toEqual([
|
||||
"zapier",
|
||||
"github",
|
||||
"slack",
|
||||
expect(res.body.apps.map((app: { slug: string }) => app.slug)).toEqual(expect.arrayContaining([
|
||||
"jira",
|
||||
"airtable",
|
||||
"asana",
|
||||
"notion",
|
||||
"posthog",
|
||||
"sentry",
|
||||
"zapier",
|
||||
"linear",
|
||||
"google-sheets",
|
||||
"context7",
|
||||
"composio",
|
||||
"gmail",
|
||||
]);
|
||||
expect(res.body.apps.find((app: { slug: string }) => app.slug === "gmail").availability).toEqual({
|
||||
available: false,
|
||||
reason: "Gmail is not available on this Paperclip instance yet.",
|
||||
"google-drive",
|
||||
"google-docs",
|
||||
"google-sheets",
|
||||
"google-slides",
|
||||
"google-calendar",
|
||||
"google-chat",
|
||||
"google-people",
|
||||
"google-workspace-search",
|
||||
]));
|
||||
expect(res.body.apps).toHaveLength(58);
|
||||
expect(res.body.apps.find((app: { slug: string }) => app.slug === "gmail").ownershipAvailability).toEqual({
|
||||
platform_shared: false,
|
||||
platform_provisioned: false,
|
||||
customer: true,
|
||||
dcr: true,
|
||||
});
|
||||
expect(res.body.apps.map((app: { slug: string }) => app.slug)).not.toContain("google-drive");
|
||||
expect(res.body.apps).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
|
|
@ -3448,14 +3457,16 @@ describeEmbeddedPostgres("tool access service", () => {
|
|||
slug: "zapier",
|
||||
methods: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
credentialFields: [expect.objectContaining({ key: "authorization" })],
|
||||
keyPlacement: expect.objectContaining({ location: "header", name: "Authorization" }),
|
||||
key: "generated-url",
|
||||
auth: "none",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
slug: "google-sheets",
|
||||
availability: expect.objectContaining({ available: false }),
|
||||
methods: expect.arrayContaining([
|
||||
expect.objectContaining({ key: "local", transport: "local_stdio" }),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
|
@ -3908,12 +3919,14 @@ describeEmbeddedPostgres("tool access service", () => {
|
|||
|
||||
await service.connectGalleryApp(companyB.id, {
|
||||
galleryKey: "google-sheets",
|
||||
connectionMethodKey: "local",
|
||||
name: "Company B sheets",
|
||||
configValues: { allowedSpreadsheetIds: ["shared-sheet"] },
|
||||
}, { actorType: "user", actorId: "board-b" });
|
||||
|
||||
await expect(service.connectGalleryApp(companyA.id, {
|
||||
galleryKey: "google-sheets",
|
||||
connectionMethodKey: "local",
|
||||
name: "Company A sheets",
|
||||
configValues: { allowedSpreadsheetIds: ["shared-sheet"] },
|
||||
}, { actorType: "user", actorId: "board-a" })).rejects.toMatchObject({
|
||||
|
|
@ -3936,6 +3949,7 @@ describeEmbeddedPostgres("tool access service", () => {
|
|||
|
||||
const connect = await service.connectGalleryApp(company.id, {
|
||||
galleryKey: "google-sheets",
|
||||
connectionMethodKey: "local",
|
||||
name: "Company sheets",
|
||||
configValues: { allowedSpreadsheetIds: ["sheet-with-inputs"] },
|
||||
}, { actorType: "user", actorId: "board" });
|
||||
|
|
@ -4003,11 +4017,13 @@ describeEmbeddedPostgres("tool access service", () => {
|
|||
|
||||
await service.connectGalleryApp(companyB.id, {
|
||||
galleryKey: "google-sheets",
|
||||
connectionMethodKey: "local",
|
||||
name: "Company B sheets",
|
||||
configValues: { allowedSpreadsheetIds: ["company-b-sheet"] },
|
||||
}, { actorType: "user", actorId: "board-b" });
|
||||
const companyAConnection = await service.connectGalleryApp(companyA.id, {
|
||||
galleryKey: "google-sheets",
|
||||
connectionMethodKey: "local",
|
||||
name: "Company A sheets",
|
||||
configValues: { allowedSpreadsheetIds: ["company-a-sheet"] },
|
||||
}, { actorType: "user", actorId: "board-a" });
|
||||
|
|
@ -4088,11 +4104,13 @@ describeEmbeddedPostgres("tool access service", () => {
|
|||
|
||||
const first = await service.connectGalleryApp(company.id, {
|
||||
galleryKey: "google-sheets",
|
||||
connectionMethodKey: "local",
|
||||
name: "First sheets",
|
||||
configValues: { allowedSpreadsheetIds: ["same-company-sheet"] },
|
||||
}, { actorType: "user", actorId: "board" });
|
||||
const second = await service.connectGalleryApp(company.id, {
|
||||
galleryKey: "google-sheets",
|
||||
connectionMethodKey: "local",
|
||||
name: "Second sheets",
|
||||
configValues: { allowedSpreadsheetIds: ["same-company-sheet"] },
|
||||
}, { actorType: "user", actorId: "board" });
|
||||
|
|
@ -6264,10 +6282,10 @@ describeEmbeddedPostgres("tool access service", () => {
|
|||
runtimeConfig: {},
|
||||
}).returning();
|
||||
|
||||
const connect = await withGalleryServerUrl("zapier", PUBLIC_MCP_FIXTURE_URL, () =>
|
||||
const connect = await withGalleryServerUrl("github", PUBLIC_MCP_FIXTURE_URL, () =>
|
||||
service.connectGalleryApp(company.id, {
|
||||
galleryKey: "zapier",
|
||||
name: "Zapier workspace",
|
||||
galleryKey: "github",
|
||||
name: "GitHub workspace",
|
||||
credentialValues: { "credentials.authorization": "zap-secret" },
|
||||
}, { actorType: "user", actorId: "board" }));
|
||||
|
||||
|
|
@ -6281,11 +6299,11 @@ describeEmbeddedPostgres("tool access service", () => {
|
|||
expect(connect.connection).toMatchObject({
|
||||
status: "draft",
|
||||
enabled: false,
|
||||
config: expect.objectContaining({ sourceTemplateKey: "zapier", quarantineNewEntries: false }),
|
||||
config: expect.objectContaining({ sourceTemplateKey: "github", quarantineNewEntries: false }),
|
||||
credentialSecretRefs: [
|
||||
expect.objectContaining({
|
||||
configPath: "credentials.authorization",
|
||||
label: "Zapier MCP token",
|
||||
label: "GitHub token",
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
|
@ -6655,11 +6673,12 @@ describeEmbeddedPostgres("tool access service", () => {
|
|||
runtimeConfig: {},
|
||||
}).returning();
|
||||
|
||||
const connect = await service.connectGalleryApp(company.id, {
|
||||
galleryKey: "zapier",
|
||||
name: "Zapier rollback",
|
||||
credentialValues: { "credentials.authorization": "zap-secret" },
|
||||
}, { actorType: "user", actorId: "board" });
|
||||
const connect = await withGalleryServerUrl("github", PUBLIC_MCP_FIXTURE_URL, () =>
|
||||
service.connectGalleryApp(company.id, {
|
||||
galleryKey: "github",
|
||||
name: "GitHub rollback",
|
||||
credentialValues: { "credentials.authorization": "github-secret" },
|
||||
}, { actorType: "user", actorId: "board" }));
|
||||
const listEntry = connect.catalog.find((entry) => entry.toolName === "list_zaps")!;
|
||||
const updateEntry = connect.catalog.find((entry) => entry.toolName === "update_zap")!;
|
||||
const firstFinish = await service.finishGalleryAppConnection(company.id, connect.connectionId, {
|
||||
|
|
@ -6729,10 +6748,10 @@ describeEmbeddedPostgres("tool access service", () => {
|
|||
{ name: "update_zap", description: "Update", inputSchema: { type: "object", properties: {} }, annotations: { readOnlyHint: false } },
|
||||
]);
|
||||
|
||||
const connect = await withGalleryServerUrl("zapier", PUBLIC_MCP_FIXTURE_URL, () =>
|
||||
const connect = await withGalleryServerUrl("github", PUBLIC_MCP_FIXTURE_URL, () =>
|
||||
service.connectGalleryApp(company.id, {
|
||||
galleryKey: "zapier",
|
||||
name: "Zapier reconnect",
|
||||
galleryKey: "github",
|
||||
name: "GitHub reconnect",
|
||||
credentialValues: { "credentials.authorization": "old-secret" },
|
||||
}, { actorType: "user", actorId: "board" }));
|
||||
|
||||
|
|
@ -8637,6 +8656,16 @@ describe("normalizeConnectionMethodConfig", () => {
|
|||
const posthog = getConnectableAppDefinition("posthog")!;
|
||||
const apiKeyMethod = posthog.methods.find((method) => method.key === "mcp-api-key")!;
|
||||
|
||||
it("builds a concrete Shopify endpoint from the validated store domain", () => {
|
||||
const shopifyMethod = getConnectableAppDefinition("shopify")!.methods[0]!;
|
||||
expect(normalizeConnectionMethodConfig(shopifyMethod, {
|
||||
storeDomain: "paperclip-demo.myshopify.com",
|
||||
})).toEqual({
|
||||
values: { storeDomain: "paperclip-demo.myshopify.com" },
|
||||
url: "https://paperclip-demo.myshopify.com/api/mcp",
|
||||
});
|
||||
});
|
||||
|
||||
it("uses the broad PostHog catalog when optional advanced filters are untouched", () => {
|
||||
expect(normalizeConnectionMethodConfig(apiKeyMethod, {
|
||||
projectId: "12345",
|
||||
|
|
@ -8672,7 +8701,7 @@ describe("normalizeConnectionMethodConfig", () => {
|
|||
expect(() => normalizeConnectionMethodConfig(apiKeyMethod, {
|
||||
projectId: "not-a-project",
|
||||
features: "insights",
|
||||
})).toThrow("Project ID has an invalid value");
|
||||
})).toThrow("Pin to project ID has an invalid value");
|
||||
expect(() => normalizeConnectionMethodConfig(apiKeyMethod, {
|
||||
projectId: "12345",
|
||||
features: "insights",
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ import type {
|
|||
UpdateToolProfileWithEntries,
|
||||
UnbindToolProfileBinding,
|
||||
} from "@paperclipai/shared";
|
||||
import { CLASS3_STATIC_LEASE_ALLOWLIST, credentialConfigPath, getAvailableConnectionMethod, getAvailableConnectionMethods, getConnectableAppDefinition, isToolConnectionAttentionHealth, recommendedDefaultsForApp } from "@paperclipai/shared";
|
||||
import { CLASS3_STATIC_LEASE_ALLOWLIST, credentialConfigPath, getAvailableConnectionMethod, getAvailableConnectionMethods, getConnectableAppDefinition, isToolConnectionAttentionHealth, recommendedDefaultsForApp, resolveConnectionMethodServerUrl } from "@paperclipai/shared";
|
||||
import {
|
||||
checkMcpRemoteHeaderName,
|
||||
checkMcpRemoteHeaderValue,
|
||||
|
|
@ -854,7 +854,11 @@ export function normalizeConnectionMethodConfig(
|
|||
}
|
||||
}
|
||||
|
||||
const endpoint = method.defaults?.serverUrl ? new URL(method.defaults.serverUrl) : null;
|
||||
const resolvedServerUrl = resolveConnectionMethodServerUrl(method, values);
|
||||
if (method.defaults?.serverUrlTemplate && !resolvedServerUrl) {
|
||||
throw badRequest("Missing or invalid connection settings for the server URL");
|
||||
}
|
||||
const endpoint = resolvedServerUrl ? new URL(resolvedServerUrl) : null;
|
||||
const headers: Record<string, string> = {};
|
||||
for (const field of fields) {
|
||||
const transport = field.transport;
|
||||
|
|
|
|||
|
|
@ -1088,11 +1088,10 @@ describe("OnboardingWizard restore-gate (stale localStorage across accounts)", (
|
|||
const deny = () => {
|
||||
throw new DOMException("The operation is insecure.", "SecurityError");
|
||||
};
|
||||
const getItem = vi.spyOn(Storage.prototype, "getItem").mockImplementation(deny);
|
||||
const removeItem = vi
|
||||
.spyOn(Storage.prototype, "removeItem")
|
||||
.mockImplementation(deny);
|
||||
const setItem = vi.spyOn(Storage.prototype, "setItem").mockImplementation(deny);
|
||||
// Deny the browser boundary itself. Spying on a Storage object or
|
||||
// prototype is not portable: DOM implementations may return a fresh
|
||||
// wrapper and Node exposes a separate experimental Storage global.
|
||||
const localStorage = vi.spyOn(window, "localStorage", "get").mockImplementation(deny);
|
||||
mockCompany.companies = [{ id: "c1", name: "My Co", issuePrefix: "MC" }];
|
||||
mockCompany.loading = false;
|
||||
|
||||
|
|
@ -1106,14 +1105,12 @@ describe("OnboardingWizard restore-gate (stale localStorage across accounts)", (
|
|||
});
|
||||
await flushReact();
|
||||
|
||||
expect(getItem).toHaveBeenCalled();
|
||||
expect(localStorage).toHaveBeenCalled();
|
||||
// It mounted: the wizard is open with no draft, rather than the render
|
||||
// throwing on the way in.
|
||||
expect(document.body.textContent).not.toBe("");
|
||||
|
||||
getItem.mockRestore();
|
||||
removeItem.mockRestore();
|
||||
setItem.mockRestore();
|
||||
localStorage.mockRestore();
|
||||
await act(async () => {
|
||||
root.unmount();
|
||||
});
|
||||
|
|
@ -1171,9 +1168,7 @@ describe("OnboardingWizard restore-gate (stale localStorage across accounts)", (
|
|||
const deny = () => {
|
||||
throw new DOMException("The operation is insecure.", "SecurityError");
|
||||
};
|
||||
const getItem = vi.spyOn(Storage.prototype, "getItem").mockImplementation(deny);
|
||||
const removeItem = vi.spyOn(Storage.prototype, "removeItem").mockImplementation(deny);
|
||||
const setItem = vi.spyOn(Storage.prototype, "setItem").mockImplementation(deny);
|
||||
const localStorage = vi.spyOn(window, "localStorage", "get").mockImplementation(deny);
|
||||
mockCompany.companies = [{ id: "c1", name: "My Co", issuePrefix: "MC" }];
|
||||
mockCompany.loading = false;
|
||||
|
||||
|
|
@ -1198,9 +1193,7 @@ describe("OnboardingWizard restore-gate (stale localStorage across accounts)", (
|
|||
|
||||
expect(mockDialog.closeOnboarding).toHaveBeenCalled();
|
||||
|
||||
getItem.mockRestore();
|
||||
removeItem.mockRestore();
|
||||
setItem.mockRestore();
|
||||
localStorage.mockRestore();
|
||||
await act(async () => {
|
||||
root.unmount();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -211,21 +211,21 @@ Propose, don't decide. Keep it conversational.`;
|
|||
const onboardingDraftStorage = {
|
||||
read(): string | null {
|
||||
try {
|
||||
return localStorage.getItem(ONBOARDING_STORAGE_KEY);
|
||||
return window.localStorage.getItem(ONBOARDING_STORAGE_KEY);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
write(value: string): void {
|
||||
try {
|
||||
localStorage.setItem(ONBOARDING_STORAGE_KEY, value);
|
||||
window.localStorage.setItem(ONBOARDING_STORAGE_KEY, value);
|
||||
} catch {
|
||||
// Storage unavailable: the draft is simply not resumable this session.
|
||||
}
|
||||
},
|
||||
clear(): void {
|
||||
try {
|
||||
localStorage.removeItem(ONBOARDING_STORAGE_KEY);
|
||||
window.localStorage.removeItem(ONBOARDING_STORAGE_KEY);
|
||||
} catch {
|
||||
// Nothing to do. A draft that cannot be cleared is re-rejected on the
|
||||
// next load by the same ownership check that rejected it here.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// @vitest-environment jsdom
|
||||
|
||||
import { flushSync } from "react-dom";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { act } from "react";
|
||||
import { createRoot, type Root } from "react-dom/client";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { CONNECTABLE_APP_DEFINITIONS } from "@paperclipai/shared";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
|
@ -23,6 +23,7 @@ const mockSearch = vi.hoisted(() => ({ value: "" }));
|
|||
const mockParams = vi.hoisted(() => ({ appKey: undefined as string | undefined }));
|
||||
|
||||
const ZAPIER = CONNECTABLE_APP_DEFINITIONS.find((app) => app.slug === "zapier")!;
|
||||
const MEM0 = CONNECTABLE_APP_DEFINITIONS.find((app) => app.slug === "mem0")!;
|
||||
const NOTION = CONNECTABLE_APP_DEFINITIONS.find((app) => app.slug === "notion")!;
|
||||
const POSTHOG = CONNECTABLE_APP_DEFINITIONS.find((app) => app.slug === "posthog")!;
|
||||
const GOOGLE_SHEETS = CONNECTABLE_APP_DEFINITIONS.find((app) => app.slug === "google-sheets")!;
|
||||
|
|
@ -74,14 +75,6 @@ vi.mock("@/context/ToastContext", () => ({
|
|||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
|
||||
async function act(callback: () => void | Promise<void>) {
|
||||
let result: void | Promise<void> = undefined;
|
||||
flushSync(() => {
|
||||
result = callback();
|
||||
});
|
||||
await result;
|
||||
}
|
||||
|
||||
async function flushReact() {
|
||||
await act(async () => {
|
||||
await Promise.resolve();
|
||||
|
|
@ -157,12 +150,14 @@ async function gotoLinkFrame(container: HTMLDivElement, url: string) {
|
|||
|
||||
describe("AppsConnect — Connect with a link (M4 frame)", () => {
|
||||
let container: HTMLDivElement;
|
||||
let mountedRoot: Root | null;
|
||||
|
||||
beforeEach(() => {
|
||||
mockSearch.value = "";
|
||||
mockParams.appKey = undefined;
|
||||
container = document.createElement("div");
|
||||
document.body.appendChild(container);
|
||||
mountedRoot = null;
|
||||
listGalleryMock.mockResolvedValue({
|
||||
apps: [
|
||||
ZAPIER,
|
||||
|
|
@ -195,7 +190,10 @@ describe("AppsConnect — Connect with a link (M4 frame)", () => {
|
|||
]);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
afterEach(async () => {
|
||||
if (mountedRoot) {
|
||||
await act(async () => mountedRoot?.unmount());
|
||||
}
|
||||
document.body.removeChild(container);
|
||||
document.body.innerHTML = "";
|
||||
vi.clearAllMocks();
|
||||
|
|
@ -203,6 +201,7 @@ describe("AppsConnect — Connect with a link (M4 frame)", () => {
|
|||
|
||||
async function render(queryClient?: QueryClient, byoOnly = false) {
|
||||
const root = createRoot(container);
|
||||
mountedRoot = root;
|
||||
const client = queryClient ?? new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
await act(async () => {
|
||||
root.render(
|
||||
|
|
@ -259,14 +258,15 @@ describe("AppsConnect — Connect with a link (M4 frame)", () => {
|
|||
// -------------------------------------------------------------------------
|
||||
|
||||
it("asks both access questions before the credential and defaults per auth kind", async () => {
|
||||
mockParams.appKey = "zapier";
|
||||
mockParams.appKey = "mem0";
|
||||
listGalleryMock.mockResolvedValueOnce({ apps: [MEM0] });
|
||||
await render();
|
||||
|
||||
expect(container.textContent).toContain("Access");
|
||||
expect(container.textContent).toContain("Who is this credential for?");
|
||||
expect(container.textContent).toContain("Which agents can use this connection?");
|
||||
// Nothing about the credential is on screen yet.
|
||||
expect(container.textContent).not.toContain("Connect Zapier");
|
||||
expect(container.textContent).not.toContain("Connect Mem0");
|
||||
|
||||
const radios = Array.from(document.body.querySelectorAll('[role="radio"]'));
|
||||
const justMe = radios.find((r) => r.textContent?.includes("Just me"));
|
||||
|
|
@ -282,7 +282,8 @@ describe("AppsConnect — Connect with a link (M4 frame)", () => {
|
|||
});
|
||||
|
||||
it("blocks Continue until Agents I pick has at least one agent", async () => {
|
||||
mockParams.appKey = "zapier";
|
||||
mockParams.appKey = "mem0";
|
||||
listGalleryMock.mockResolvedValueOnce({ apps: [MEM0] });
|
||||
await render();
|
||||
|
||||
// "Agents I pick" with nothing picked is not a usable connection, so the
|
||||
|
|
@ -300,7 +301,8 @@ describe("AppsConnect — Connect with a link (M4 frame)", () => {
|
|||
});
|
||||
|
||||
it("keeps the access selections when the wizard moves backward", async () => {
|
||||
mockParams.appKey = "zapier";
|
||||
mockParams.appKey = "mem0";
|
||||
listGalleryMock.mockResolvedValueOnce({ apps: [MEM0] });
|
||||
await render();
|
||||
|
||||
await act(async () => {
|
||||
|
|
@ -320,7 +322,7 @@ describe("AppsConnect — Connect with a link (M4 frame)", () => {
|
|||
buttonByText("Save and continue")?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
});
|
||||
await flushReact();
|
||||
expect(container.textContent).toContain("Connect Zapier");
|
||||
expect(container.textContent).toContain("Connect Mem0");
|
||||
|
||||
await act(async () => {
|
||||
buttonByText("Back")?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
|
|
@ -340,7 +342,7 @@ describe("AppsConnect — Connect with a link (M4 frame)", () => {
|
|||
*
|
||||
* A homogeneous fixture cannot tell those two apart: if every app on screen
|
||||
* is API-key-only, a blanket rule and a per-method predicate produce exactly
|
||||
* the same DOM. So both live in one gallery here — Zapier, whose only method
|
||||
* the same DOM. So both live in one gallery here — Mem0, whose only method
|
||||
* is an API key, and PostHog, whose methods are identity-bearing sign-in plus
|
||||
* a key its own label calls *personal*. The two mounts must disagree about
|
||||
* the default, which no blanket rule can do.
|
||||
|
|
@ -350,7 +352,7 @@ describe("AppsConnect — Connect with a link (M4 frame)", () => {
|
|||
* grant, so disabling it would describe the product wrongly.
|
||||
*/
|
||||
it("decides the identity default per method, and keeps a personal key submittable", async () => {
|
||||
listGalleryMock.mockResolvedValue({ apps: [ZAPIER, POSTHOG] });
|
||||
listGalleryMock.mockResolvedValue({ apps: [MEM0, POSTHOG] });
|
||||
|
||||
const identityChoices = () => {
|
||||
const radios = Array.from(
|
||||
|
|
@ -365,22 +367,22 @@ describe("AppsConnect — Connect with a link (M4 frame)", () => {
|
|||
// --- API-key-only method: shared by default, personal still offered ------
|
||||
let root = await render();
|
||||
await act(async () => {
|
||||
buttonContaining("Zapier")?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
buttonContaining("Mem0")?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
});
|
||||
await flushReact();
|
||||
|
||||
const zapier = identityChoices();
|
||||
expect(zapier.wholeOrg?.getAttribute("aria-checked")).toBe("true");
|
||||
expect(zapier.justMe?.getAttribute("aria-checked")).toBe("false");
|
||||
const mem0 = identityChoices();
|
||||
expect(mem0.wholeOrg?.getAttribute("aria-checked")).toBe("true");
|
||||
expect(mem0.justMe?.getAttribute("aria-checked")).toBe("false");
|
||||
// Present, and genuinely selectable — not the disabled-with-reason state.
|
||||
expect(zapier.justMe).toBeTruthy();
|
||||
expect(zapier.justMe?.disabled).toBe(false);
|
||||
expect(mem0.justMe).toBeTruthy();
|
||||
expect(mem0.justMe?.disabled).toBe(false);
|
||||
expect(document.body.textContent).not.toContain(
|
||||
"This connection method supports a shared organization credential only.",
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
zapier.justMe?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
mem0.justMe?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
});
|
||||
await flushReact();
|
||||
await act(async () => {
|
||||
|
|
@ -395,7 +397,7 @@ describe("AppsConnect — Connect with a link (M4 frame)", () => {
|
|||
await flushReact();
|
||||
|
||||
const keyField = container.querySelector<HTMLInputElement>("input[type=password]");
|
||||
await act(async () => setInputValue(keyField!, "zapier-personal-token"));
|
||||
await act(async () => setInputValue(keyField!, "mem0-personal-token"));
|
||||
await flushReact();
|
||||
await act(async () => {
|
||||
buttonByText("Connect")?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
|
|
@ -406,7 +408,7 @@ describe("AppsConnect — Connect with a link (M4 frame)", () => {
|
|||
// a personal grant. A disabled "Just me" would make this unreachable.
|
||||
expect(connectAppMock).toHaveBeenCalledTimes(1);
|
||||
expect(connectAppMock.mock.calls[0]?.[1]).toMatchObject({
|
||||
galleryKey: "zapier",
|
||||
galleryKey: "mem0",
|
||||
grantKind: "user",
|
||||
});
|
||||
|
||||
|
|
@ -522,12 +524,12 @@ describe("AppsConnect — Connect with a link (M4 frame)", () => {
|
|||
});
|
||||
await flushReact();
|
||||
|
||||
const projectInput = container.querySelector<HTMLInputElement>('input[placeholder="12345"]');
|
||||
const keyInput = container.querySelector<HTMLInputElement>('input[type="password"]');
|
||||
const advanced = buttonByText("Advanced");
|
||||
expect(projectInput).toBeTruthy();
|
||||
expect(keyInput).toBeTruthy();
|
||||
expect(container.querySelector('[role="switch"]')?.getAttribute("aria-checked")).toBe("false");
|
||||
expect(container.querySelector<HTMLInputElement>('input[placeholder="Optional numeric project ID"]'))
|
||||
.toBeNull();
|
||||
expect(container.querySelector('[role="switch"]')).toBeNull();
|
||||
expect(advanced?.getAttribute("aria-expanded")).toBe("false");
|
||||
expect(container.textContent).not.toContain("Feature groups");
|
||||
expect(container.textContent).not.toContain("Individual tools");
|
||||
|
|
@ -541,7 +543,12 @@ describe("AppsConnect — Connect with a link (M4 frame)", () => {
|
|||
expect(advanced?.getAttribute("aria-expanded")).toBe("true");
|
||||
expect(container.textContent).toContain("Feature groups");
|
||||
expect(container.textContent).toContain("Individual tools");
|
||||
expect(container.textContent).toContain("Tool response mode");
|
||||
expect(container.textContent).not.toContain("Tool response mode");
|
||||
const projectInput = container.querySelector<HTMLInputElement>(
|
||||
'input[placeholder="Optional numeric project ID"]',
|
||||
);
|
||||
expect(projectInput).toBeTruthy();
|
||||
expect(container.querySelector('[role="switch"]')?.getAttribute("aria-checked")).toBe("false");
|
||||
|
||||
await act(async () => {
|
||||
setInputValue(projectInput!, "12345");
|
||||
|
|
@ -1222,10 +1229,11 @@ describe("AppsConnect — Connect with a link (M4 frame)", () => {
|
|||
});
|
||||
|
||||
it("leaving the default name connects with the app name", async () => {
|
||||
listGalleryMock.mockResolvedValueOnce({ apps: [MEM0] });
|
||||
await render();
|
||||
|
||||
await act(async () => {
|
||||
buttonContaining("Zapier")?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
buttonContaining("Mem0")?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
});
|
||||
await flushReact();
|
||||
await passAccessStep();
|
||||
|
|
@ -1239,21 +1247,22 @@ describe("AppsConnect — Connect with a link (M4 frame)", () => {
|
|||
await flushReact();
|
||||
|
||||
expect(connectAppMock).toHaveBeenCalledTimes(1);
|
||||
expect(mockNavigate).toHaveBeenCalledWith("/apps/connect?byo=1&appKey=zapier&stage=access");
|
||||
expect(mockNavigate).toHaveBeenCalledWith("/apps/connect?byo=1&appKey=mem0&stage=access");
|
||||
const [, input] = connectAppMock.mock.calls[0];
|
||||
expect(input).toMatchObject({ galleryKey: "zapier", name: "Zapier" });
|
||||
expect(input).toMatchObject({ galleryKey: "mem0", name: "Mem0" });
|
||||
});
|
||||
|
||||
it("a custom name in the gallery step is sent to the connect mutation", async () => {
|
||||
listGalleryMock.mockResolvedValueOnce({ apps: [MEM0] });
|
||||
await render();
|
||||
|
||||
await act(async () => {
|
||||
buttonContaining("Zapier")?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
buttonContaining("Mem0")?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
});
|
||||
await flushReact();
|
||||
await passAccessStep();
|
||||
|
||||
await act(async () => setInputValue(nameInputFrom(container)!, "Zapier (stdio smoke)"));
|
||||
await act(async () => setInputValue(nameInputFrom(container)!, "Mem0 (stdio smoke)"));
|
||||
const keyField = container.querySelector<HTMLInputElement>("input[type=password]");
|
||||
await act(async () => setInputValue(keyField!, "secret-key"));
|
||||
await flushReact();
|
||||
|
|
@ -1264,7 +1273,7 @@ describe("AppsConnect — Connect with a link (M4 frame)", () => {
|
|||
|
||||
expect(connectAppMock).toHaveBeenCalledTimes(1);
|
||||
const [, input] = connectAppMock.mock.calls[0];
|
||||
expect(input).toMatchObject({ galleryKey: "zapier", name: "Zapier (stdio smoke)" });
|
||||
expect(input).toMatchObject({ galleryKey: "mem0", name: "Mem0 (stdio smoke)" });
|
||||
});
|
||||
|
||||
it("a custom name on the Google Sheets step is sent to the connect mutation", async () => {
|
||||
|
|
@ -1346,12 +1355,14 @@ describe("AppsConnect — Connect with a link (M4 frame)", () => {
|
|||
*/
|
||||
describe("AppsConnect — guided generic MCP flow (PAP-17087)", () => {
|
||||
let container: HTMLDivElement;
|
||||
let mountedRoot: Root | null;
|
||||
|
||||
beforeEach(() => {
|
||||
mockSearch.value = "";
|
||||
mockParams.appKey = undefined;
|
||||
container = document.createElement("div");
|
||||
document.body.appendChild(container);
|
||||
mountedRoot = null;
|
||||
listGalleryMock.mockResolvedValue({ apps: [ZAPIER] });
|
||||
listApplicationsMock.mockResolvedValue({ applications: [] });
|
||||
listConnectionsMock.mockResolvedValue({ connections: [] });
|
||||
|
|
@ -1373,7 +1384,10 @@ describe("AppsConnect — guided generic MCP flow (PAP-17087)", () => {
|
|||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
afterEach(async () => {
|
||||
if (mountedRoot) {
|
||||
await act(async () => mountedRoot?.unmount());
|
||||
}
|
||||
document.body.removeChild(container);
|
||||
document.body.innerHTML = "";
|
||||
vi.clearAllMocks();
|
||||
|
|
@ -1381,6 +1395,7 @@ describe("AppsConnect — guided generic MCP flow (PAP-17087)", () => {
|
|||
|
||||
async function render() {
|
||||
const root = createRoot(container);
|
||||
mountedRoot = root;
|
||||
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
await act(async () => {
|
||||
root.render(
|
||||
|
|
|
|||
|
|
@ -1903,8 +1903,12 @@ function KeyStep({
|
|||
(f) => f.required === false || (values[f.configPath]?.trim().length ?? 0) > 0,
|
||||
);
|
||||
const configFields = [...(method?.tenantFields ?? []), ...(method?.extensionFields ?? [])];
|
||||
const standardConfigFields = configFields.filter((field) => field.advanced !== true);
|
||||
const advancedConfigFields = configFields.filter((field) => field.advanced === true);
|
||||
const standardConfigFields = configFields.filter(
|
||||
(field) => field.hidden !== true && field.advanced !== true,
|
||||
);
|
||||
const advancedConfigFields = configFields.filter(
|
||||
(field) => field.hidden !== true && field.advanced === true,
|
||||
);
|
||||
const [advancedOpen, setAdvancedOpen] = useState(false);
|
||||
const configFilled = configFields.every((field) => {
|
||||
if (!field.required) return true;
|
||||
|
|
|
|||
Loading…
Reference in New Issue