Direct answer

Avoid lock-in by keeping source credentials, permission policy, and tool naming outside the agent client. Use MCP as the contract between clients and your systems, then make each client point at the same remote integration endpoint instead of rebuilding per-agent connectors. The hard part is not the JSON config; it is deciding which layer owns OAuth, read/write grants, failure handling, and tool inventory.

Where lock-in really appears

AI vendor lock-in at the integration layer usually starts quietly. A team adds GitHub to one coding agent, a database helper to another, a Drive connector to a third, and each client becomes the place where source URLs, API keys, OAuth tokens, tool names, and approval habits live. The model may be easy to swap, but the operational boundary is no longer replaceable because the integrations sit inside one vendor's configuration surface.

MCP gives teams a better boundary to aim at. A server exposes tools, clients discover them with JSON-RPC tools/list, and clients invoke them with tools/call. The transport may be local stdio or Streamable HTTP, where messages are posted to a single MCP endpoint. That means the integration contract can be kept stable while clients change, as long as the source connections sit behind the contract instead of being duplicated inside every client.

The practical test is simple: if you replaced Claude Code with Cursor or VS Code tomorrow, which things would you need to redo? If the answer is only 'paste a new MCP endpoint into the client,' you have an agent-agnostic integration layer. If the answer includes reconnecting OAuth apps, copying database passwords, renaming tools, re-auditing write permissions, and re-teaching the agent which server owns which action, the lock-in is in your integration layer, not in the model.

Use MCP as the contract, not the storage layer

The MCP contract is deliberately small enough to use as a portability boundary. A client asks for the available tools with a request shaped like this:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list",
  "params": {}
}

For a specific action, the client calls one tool by name and passes arguments:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "linear.search_issues",
    "arguments": { "query": "label:bug state:triage" }
  }
}

Those messages are a protocol boundary, not a full governance model. The protocol can describe tool names, input schemas, icons, annotations, output content, and list changes. It cannot decide for your company where OAuth refresh tokens should live, whether an API key should reach write tools, how long a call may run, or whether two upstream servers are allowed to expose the same tool name. Those choices belong in the integration layer you control.

That distinction is what prevents a protocol from becoming another form of lock-in. Treat MCP as the interface that agents speak, then keep durable state behind it: source registrations, upstream credentials, per-source grants, audit logs, timeout policy, and the mapping from upstream tool names into names the agent can understand. A client should be disposable. The source graph should not be.

Make client configuration boring

A portable setup should look almost disappointingly small in each agent. Claude Code can add a remote HTTP MCP server with a command such as:

claude mcp add --transport http company-tools https://mcp.example.com/mcp

If the server uses project scope, the project configuration is shared with the repository; if it uses local or user scope, it stays on the user's machine. The important architectural point is that the command names one endpoint. It should not contain ten unrelated vendor tokens or a separate server entry for every SaaS product the agent might touch.

Cursor has the same portability shape through .cursor/mcp.json for project configuration or ~/.cursor/mcp.json for global configuration. A remote endpoint can be represented as one server entry:

{
  "mcpServers": {
    "company-tools": {
      "url": "https://mcp.example.com/mcp",
      "headers": {
        "Authorization": "Bearer ${env:COMPANY_MCP_KEY}"
      }
    }
  }
}

VS Code supports a workspace .vscode/mcp.json with a top-level servers object, and also documents a portable workspace .mcp.json using mcpServers. For the same remote endpoint, the VS Code format can stay equally small:

{
  "servers": {
    "company-tools": {
      "type": "http",
      "url": "https://mcp.example.com/mcp"
    }
  }
}

These files are not interchangeable byte for byte, but they should be equivalent in meaning. The integration layer is doing its job when client migration is a thin configuration translation: server name, URL, and authentication method. It is failing when every client needs its own copy of the upstream topology.

Keep credentials and permissions out of the client

The strongest lock-in happens when credentials are distributed according to client convenience. One agent gets a Linear token with write access, another gets a read-only GitHub token, a third has a database password in an environment file, and nobody can answer which user or automation actually performed an action. Moving agents then becomes a credentials project instead of a configuration change.

A better pattern is to give the agent client one credential for the integration layer, then let that layer hold or broker upstream authorization. MCP's HTTP authorization model is OAuth-based: a protected MCP server acts as a resource server, and clients use access tokens when making protected requests. That gives you a place to align authentication at the MCP boundary while keeping upstream source credentials behind it.

Permissions should be expressed at the same boundary. For example, a key for a documentation assistant might be allowed to call only read tools in Google Drive and GitHub. A release assistant might add selected write tools for Linear or deployment systems. The agent client does not need to know whether an upstream server implements those permissions as OAuth scopes, API tokens, database grants, or server-side allowlists. It needs a clear tool list, accurate schemas, and predictable failure responses.

Design rule

If changing agents requires rotating or redistributing upstream source credentials, the integration layer is too thin. If changing agents only requires issuing or moving one MCP-facing credential, the boundary is in the right place.

Handle naming, discovery, and failure once

Tool names are another lock-in surface because models learn from names and descriptions. The MCP tools specification says names are unique within a server, but aggregation can create collisions when two upstream servers both expose something generic like search or create_issue. A durable integration layer should normalize those names once, usually with source-aware names such as github/search_repositories and linear/search_issues, instead of relying on each client to resolve collisions differently.

Discovery also needs discipline. Dumping every connected source into every session makes the agent slower to choose and harder to supervise. A stable layer can keep the initial list small, expose folders or toolsets first, and reveal detailed tools only when the agent opens the source it needs. That preserves portability because the policy travels with the endpoint. You do not have to rediscover the right balance every time a new client adds a slightly different MCP UI.

Failures should be normalized in the same place. If GitHub is unreachable, the agent should see a bounded tool failure that names the upstream source and keeps the rest of the workspace usable. If an OAuth token expires, the response should distinguish authentication from a schema problem. If a write tool is withheld, the tool inventory should make that permission boundary visible enough for debugging. A migration between clients should not change the meaning of a timeout, a denied write, or an unreachable upstream.

Teams often approach this after reading narrower migration guides such as switching AI agents without rebuilding integrations or agent-agnostic architecture. The integration-layer version is more concrete: make names, discovery, credentials, and failures properties of the endpoint, not properties of the current agent.

Where SPYN3 puts the boundary

SPYN3 keeps integrations out of the agent and out of its own storage. The agent sees a standard MCP endpoint, so any client that speaks remote MCP can use it. The connected systems keep their data, since SPYN3 proxies calls rather than copying content. Leaving is also cheap on SPYN3's side: the dashboard exports your data and deletes the account, and your upstream systems are untouched either way.

Built for the next step

Keep the source layer portable

Connect systems once, then test each agent against the same endpoint.

Client support
About SPYN3

SPYN3 is a consolidated MCP layer: one hosted endpoint in front of every system you connect — MCP servers, Postgres, REST APIs, Google Drive — with tools foldered by source and read/write permissions per key. Switching agents means moving one URL. Free plan available; current capabilities are documented on the product facts page.

Sources

  1. MCP specification: tools — tool discovery, invocation and security considerations. Verified 14 Oct 2026
  2. MCP specification: transports — stdio and Streamable HTTP transport boundaries. Verified 14 Oct 2026
  3. MCP specification: authorization — OAuth-based authorization for HTTP MCP servers. Verified 14 Oct 2026
  4. Anthropic docs: Claude Code MCP setup — Claude Code MCP commands, scopes, transports and JSON configuration. Verified 14 Oct 2026
  5. VS Code documentation: add and manage MCP servers — VS Code MCP configuration, trust and logging behavior. Verified 14 Oct 2026
  6. Cursor documentation: MCP integrations — Cursor MCP configuration locations, remote URL entries, headers and approval behavior. Verified 14 Oct 2026