# Connecting Agents to Admin MCP Connect external AI agents, IDE assistants, and custom applications to Finqu's Admin Model Context Protocol (MCP) server at `https://mcp.finqu.com/mcp`. Once connected, your agent can discover available tools and perform merchant admin operations such as managing catalog items, reviewing orders, modifying themes, and configuring store settings. ## Prerequisites Before configuring your client: 1. Create an OAuth application in the Finqu merchant admin or partner portal, or obtain a valid merchant OAuth access token. See [Authentication](./authentication) and [OAuth 2.0 Guide](/get-started/authentication). 2. Ensure your token carries the required scopes for the tools your agent intends to use (for example, `catalog_read`, `sales_read`, `themes_write`). 3. Note the server endpoint: `https://mcp.finqu.com/mcp`. ## Claude Desktop Claude Desktop supports Streamable HTTP MCP servers directly. Add the Finqu Admin MCP server to your configuration file. ### Configuration path - **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json` - **Windows**: `%APPDATA%\Claude\claude_desktop_config.json` ### Configuration Add `finqu-admin` to the `mcpServers` object in `claude_desktop_config.json`: ```json { "mcpServers": { "finqu-admin": { "url": "https://mcp.finqu.com/mcp", "headers": { "Authorization": "Bearer YOUR_ACCESS_TOKEN" } } } } ``` Restart Claude Desktop after saving the file. Claude displays the Finqu tools in its tool catalog (hammer icon). > **Info:** When asking Claude to perform tasks, instruct it to call `list_channels` before using channel-specific tools such as theme or navigation tools. ## Cursor and Codex Configure Cursor or Codex at the project level to give coding agents access to Finqu admin operations during development. ### Project configuration (`.cursor/mcp.json`) Create `.cursor/mcp.json` in your repository root: ```json { "mcpServers": { "finqu-admin": { "url": "https://mcp.finqu.com/mcp", "headers": { "Authorization": "Bearer YOUR_ACCESS_TOKEN" } } } } ``` ### Agent operating rules (`.cursor/rules/finqu-admin.mdc`) Create an agent rule file in `.cursor/rules/finqu-admin.mdc` to guide the model on safe tool usage: ```markdown --- description: Rules for using Finqu Admin MCP tools globs: * alwaysApply: true --- # Finqu Admin MCP Rules When interacting with the Finqu Admin MCP server: 1. **Session initialization**: - Call `get_profile` to verify merchant context and granted scopes. - Call `list_channels` before issuing any channel-scoped tool. Store `channel_id` for subsequent calls. 2. **Channel requirements**: - Theme tools, navigation tools, and page tools require a `webstore` channel type. - Do not pass non-webstore channel IDs to theme or storefront tools. 3. **Safety and confirmation**: - Always confirm destructive operations with the user before calling write tools (e.g., deleting product variants, changing prices, unpublishing items, modifying theme files). - Display a preview of proposed changes and wait for user confirmation. 4. **Error handling**: - Inspect error responses with `{ "status": "error", "response": { "code", "message", "suggestions" } }`. - If a tool reports `revision_mismatch` or `release_missing`, refresh the state before retrying. ``` ## More editors and agents These clients can connect to the same Admin MCP endpoint. Authorize access to the merchant account using [Admin MCP authentication](./authentication). If your client requires an explicit OAuth client or bearer token, configure it using that guide before calling store tools. ### VS Code Add this to `.vscode/mcp.json` in your project: ```json { "servers": { "finqu": { "type": "http", "url": "https://mcp.finqu.com/mcp" } } } ``` Use **Start** in the MCP configuration editor, complete authorization, and use the tools in Copilot's agent mode. VS Code uses a top-level `servers` object. See [VS Code's MCP setup guide](https://code.visualstudio.com/docs/agent-customization/mcp-servers). ### Gemini CLI Add this to `.gemini/settings.json` in your project: ```json { "mcpServers": { "finqu": { "httpUrl": "https://mcp.finqu.com/mcp" } } } ``` Run `/mcp auth finqu` inside Gemini CLI to authorize the connection. The `httpUrl` field selects Streamable HTTP; `url` selects the older SSE transport. See [Gemini CLI's MCP setup guide](https://geminicli.com/docs/tools/mcp-server/). ### Cline Open Cline's MCP Servers panel and its `cline_mcp_settings.json` configuration. Add this server, replacing `YOUR_ACCESS_TOKEN` with an authorized merchant access token from the [authentication guide](./authentication): ```json { "mcpServers": { "finqu": { "type": "streamableHttp", "url": "https://mcp.finqu.com/mcp", "headers": { "Authorization": "Bearer YOUR_ACCESS_TOKEN" } } } } ``` Set the transport explicitly to `streamableHttp`. Keep access tokens in your local client configuration. See [Cline's MCP setup guide](https://github.com/cline/cline/blob/main/docs/mcp/mcp-overview.mdx). ## Custom Agent Clients Build custom agent harnesses using the official Model Context Protocol SDKs in TypeScript or Python. ### Session startup sequence Every agent session must follow a standard initialization lifecycle: ```mermaid sequenceDiagram participant Agent as Agent Client participant MCP as Admin MCP (https://mcp.finqu.com/mcp) participant Auth as Auth Server (account.finqu.com) Note over Agent,MCP: 1. Discovery (optional for pre-authenticated clients) Agent->>MCP: GET /mcp (without token) MCP-->>Agent: 401 Unauthorized + WWW-Authenticate (RFC 9728) Agent->>Auth: Exchange credentials via OAuth 2.0 (RFC 8414) Auth-->>Agent: Access Token Note over Agent,MCP: 2. Session Startup Agent->>MCP: tools/list (with Authorization header) MCP-->>Agent: Available tool schemas Agent->>MCP: tools/call { name: "get_profile" } MCP-->>Agent: Merchant ID, currency, staff profile, scopes Agent->>MCP: tools/call { name: "list_channels" } MCP-->>Agent: Channel list (IDs, types, names) Note over Agent: Cache channel_id in agent session memory ``` ### TypeScript SDK (`@modelcontextprotocol/sdk`) Install the official client SDK: ```bash npm install @modelcontextprotocol/sdk ``` The following example shows how to: 1. Discover OAuth metadata dynamically via RFC 9728 and RFC 8414 if needed. 2. Initialize the `StreamableHttpClientTransport`. 3. Connect the MCP `Client`. 4. Execute the startup sequence (`tools/list` -> `get_profile` -> `list_channels`). ```typescript import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { StreamableHttpClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; interface FinquChannel { id: number; name: string; type: string; store: Record; } interface FinquProfile { user: { id: number; name: string; email: string }; merchant: { id: number; name: string; hostname: string; currency_id: number }; scopes: string[]; } /** * Discover OAuth authorization server endpoints using RFC 9728 and RFC 8414. */ async function discoverOAuthMetadata(mcpUrl: string): Promise<{ authorizationEndpoint: string; tokenEndpoint: string; }> { // Step 1: Probe MCP endpoint for RFC 9728 resource metadata const probeResponse = await fetch(mcpUrl, { method: "GET" }); const authHeader = probeResponse.headers.get("www-authenticate") || ""; const match = authHeader.match(/resource_metadata="([^"]+)"/); const resourceMetadataUrl = match ? match[1] : "https://mcp.finqu.com/.well-known/oauth-protected-resource/mcp"; // Step 2: Fetch protected resource metadata const resourceRes = await fetch(resourceMetadataUrl); const resourceData = await resourceRes.json(); const authServerBase = resourceData.authorization_servers?.[0] || "https://account.finqu.com"; // Step 3: Fetch RFC 8414 authorization server metadata const serverMetaRes = await fetch(`${authServerBase}/.well-known/oauth-authorization-server`); const serverMeta = await serverMetaRes.json(); return { authorizationEndpoint: serverMeta.authorization_endpoint, tokenEndpoint: serverMeta.token_endpoint, }; } /** * Initialize and run a Finqu Admin MCP agent session. */ async function runAdminAgent(accessToken: string) { const mcpUrl = new URL("https://mcp.finqu.com/mcp"); // Initialize Streamable HTTP transport with Bearer token const transport = new StreamableHttpClientTransport(mcpUrl, { requestInit: { headers: { Authorization: `Bearer ${accessToken}`, }, }, }); const client = new Client({ name: "finqu-custom-agent", version: "1.0.0" }, { capabilities: {} }); await client.connect(transport); console.log("Connected to Finqu Admin MCP"); // Step 1: Discover available tools const toolsResult = await client.listTools(); console.log(`Discovered ${toolsResult.tools.length} admin tools`); // Step 2: Get merchant profile and active scopes const profileResult = await client.callTool({ name: "get_profile", arguments: {}, }); const profile = profileResult.content[0].text ? (JSON.parse(profileResult.content[0].text) as FinquProfile) : null; console.log(`Authenticated as merchant: ${profile?.merchant.name} (ID: ${profile?.merchant.id})`); console.log(`Granted scopes: ${profile?.scopes.join(", ")}`); // Step 3: Discover sales channels const channelsResult = await client.callTool({ name: "list_channels", arguments: {}, }); const channelsPayload = channelsResult.content[0].text ? (JSON.parse(channelsResult.content[0].text) as { merchant_id: number; channels: FinquChannel[] }) : { merchant_id: 0, channels: [] }; const webstoreChannel = channelsPayload.channels.find((c) => c.type === "webstore"); const defaultChannelId = webstoreChannel?.id || channelsPayload.channels[0]?.id; console.log(`Selected default channel ID: ${defaultChannelId} (${webstoreChannel?.name})`); // Example tool call requiring channel_id if (defaultChannelId) { const categoriesResult = await client.callTool({ name: "list_categories", arguments: { channel_id: defaultChannelId, limit: 10, }, }); console.log("Categories response:", categoriesResult.content[0].text); } await client.close(); } // Example invocation const token = process.env.FINQU_ACCESS_TOKEN || "YOUR_ACCESS_TOKEN"; runAdminAgent(token).catch(console.error); ``` ### Python SDK (`mcp`) Install the official Python MCP SDK: ```bash pip install mcp httpx ``` The following example establishes a session using `httpx` and `mcp.client.session.ClientSession`: ```python import asyncio import os import re import httpx from mcp import ClientSession from mcp.client.streamable_http import streamable_http_client MCP_URL = "https://mcp.finqu.com/mcp" async def discover_oauth_endpoints(mcp_url: str): """Discover OAuth 2.0 endpoints using RFC 9728 and RFC 8414.""" async with httpx.AsyncClient() as http: # Step 1: Probe unauthenticated endpoint probe = await http.get(mcp_url) auth_header = probe.headers.get("www-authenticate", "") match = re.search(r'resource_metadata="([^"]+)"', auth_header) meta_url = ( match.group(1) if match else "https://mcp.finqu.com/.well-known/oauth-protected-resource/mcp" ) # Step 2: Fetch protected resource metadata resource_res = await http.get(meta_url) resource_data = resource_res.json() auth_server = resource_data.get("authorization_servers", ["https://account.finqu.com"])[0] # Step 3: Fetch authorization server metadata server_meta_res = await http.get(f"{auth_server}/.well-known/oauth-authorization-server") server_meta = server_meta_res.json() return { "authorization_endpoint": server_meta.get("authorization_endpoint"), "token_endpoint": server_meta.get("token_endpoint"), } async def run_admin_session(access_token: str): """Connect to Finqu Admin MCP, run startup sequence, and invoke tools.""" headers = { "Authorization": f"Bearer {access_token}" } async with streamable_http_client(MCP_URL, headers=headers) as (read_stream, write_stream): async with ClientSession(read_stream, write_stream) as session: await session.initialize() print("Connected to Finqu Admin MCP") # 1. Discover tools tools_response = await session.list_tools() print(f"Discovered {len(tools_response.tools)} admin tools") # 2. Get profile profile_res = await session.call_tool("get_profile", arguments={}) print("Profile output:", profile_res.content[0].text) # 3. List channels channels_res = await session.call_tool("list_channels", arguments={}) print("Channels output:", channels_res.content[0].text) # Store channel_id for subsequent calls import json channel_data = json.loads(channels_res.content[0].text) channels = channel_data.get("channels", []) webstore = next((c for c in channels if c.get("type") == "webstore"), None) channel_id = webstore["id"] if webstore else (channels[0]["id"] if channels else None) if channel_id: print(f"Using channel_id: {channel_id}") products_res = await session.call_tool( "search_products", arguments={"channel_id": channel_id, "limit": 5} ) print("Products search result:", products_res.content[0].text) if __name__ == "__main__": token = os.environ.get("FINQU_ACCESS_TOKEN", "YOUR_ACCESS_TOKEN") asyncio.run(run_admin_session(token)) ``` ## Next steps - [Best Practices](./best-practices) — Operating rules, confirmation gates, and error recovery - [Admin MCP Overview](./admin) — Transport, channel requirements, and responses - [Authentication](./authentication) — OAuth discovery, token scopes, and permissions - [Tools Catalog](./tools) — Complete reference of available Admin MCP tools