Skip to content

MCP Integration

within-sdk tracks a vendor MCP server instance. The SDK observes supported MCP request handlers and high-level tool registrations. By default it adds a required context field to advertised tool schemas and adds get_more_tools to the tool list.

Supported servers

  • TypeScript/JavaScript: high-level McpServer and compatible low-level servers from @modelcontextprotocol/sdk 1.11+.
  • Python: official mcp servers (1.2+ and 2.x — both FastMCP and MCPServer), community FastMCP v2 and v3 servers, and compatible low-level servers.

Server setup

Create your MCP server normally, register tools, then call track().

ts
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { track } from 'within-sdk';

const server = new McpServer({
  name: 'acme',
  version: '1.0.0',
});

server.tool(
  'search_companies',
  'Search for companies by domain, revenue, or competitor.',
  SearchCompaniesSchema,
  async (args) => searchCompanies(args),
);

track(server, 'acme', {
  apiKey: process.env.WITHIN_SDK_API_KEY!,
});

export { server };
python
from mcp.server import MCPServer  # mcp 1.x: from mcp.server.fastmcp import FastMCP
from within_sdk import track, WithinOptions

server = MCPServer("acme")


@server.tool()
def search_companies(query: str) -> str:
    """Search for companies by domain, revenue, or competitor."""
    return search_companies_impl(query)


# api_key is read from the WITHIN_SDK_API_KEY environment variable
track(server, "acme", WithinOptions())

What gets instrumented

For supported MCP servers, the SDK records:

  • initialize, tools/list, and tools/call lifecycle events
  • catalog updates from tools/list
  • tool name, arguments, result, success or error state, and duration
  • MCP client and protocol metadata when present
  • the locally hashed user ID from identify() when configured
  • redacted identify() user-data traits when configured
  • get_more_tools feedback, excluded from SDK lead scoring

All payload fields are redacted and truncated before delivery to the Within API or a vendor-configured exporter (TypeScript SDK only). The SDK uses its configured Within API origin and appends the SDK-managed /v1/ingest/* paths.

Identity

Use identify() when your MCP runtime can derive a stable vendor-local user ID.

ts
track(server, 'acme', {
  apiKey: process.env.WITHIN_SDK_API_KEY!,
  async identify(_request, extra) {
    const account = await accountFromSession(extra?.sessionId);
    if (!account) return null;
    return {
      userId: account.internalAccountId,
      userData: {
        plan: account.plan,
        tier: account.tier,
      },
    };
  },
});
python
from within_sdk import UserIdentity


def identify(request, context):
    # On HTTP transports, context.request is the incoming HTTP request —
    # resolve your account from its headers, session, or verified claims.
    account = account_from_request(context)
    if not account:
        return None
    return UserIdentity(
        user_id=account.internal_account_id,
        user_data={"plan": account.plan, "tier": account.tier},
    )


track(server, "acme", WithinOptions(identify=identify))

Within hashes the user ID locally with the vendor slug, drops the user name, and sends only redacted user data.

Language note

The TypeScript identify callback may be async and receives pre-verified auth info via extra.authInfo when the server framework provides it. The Python callback is synchronous and receives the request context; on HTTP transports context.request exposes the raw request for claim verification.

Conversions stay explicit

When the same user later becomes a subscriber, report a conversion from trusted server-side checkout, webhook, or account-upgrade code using the same opaque vendor-local ID.

ts
await reportConversion({
  vendorSlug: 'acme',
  apiKey: process.env.WITHIN_SDK_API_KEY!,
}, {
  userId: account.internalAccountId,
  plan: { id: 'pro', name: 'Pro', interval: 'month' },
});
python
from within_sdk import report_conversion

report_conversion(
    "acme",
    os.environ["WITHIN_SDK_API_KEY"],
    account.internal_account_id,
    plan={"id": "pro", "name": "Pro", "interval": "month"},
)

The SDK creates the same subject locally in both paths, so Within can connect MCP activity and conversion without receiving the raw user ID. The subject hash is byte-identical across the TypeScript SDK, the Python SDK, and Within connectors — activity captured by one joins outcomes reported by another.

Reporting a conversion records a confirmed conversion and sets the SDK lead to subscriber. It does not report upgrades, churn, renewals, or billing amounts. CRM connectors can report broader outcomes; the Salesforce connector is upcoming.

Pass-through guarantee

The SDK returns the original tool result and rethrows original errors. SDK-injected context is removed before the vendor handler runs, while a vendor-declared context field is passed through unchanged (the SDK skips context injection for tools that already declare context and for complex oneOf/allOf/anyOf schemas). Reporting happens in the background. Within availability does not block the vendor tool call.

If the SDK cannot observe the server adapter safely, it returns the vendor server unchanged, logs the issue when possible, and avoids changing tool behavior. track() never throws into the host server — configuration problems disable analytics and log a warning.

Within SDK turns privacy-safe MCP activity into workflow intelligence and SDK leads.