Metadata-Version: 2.4
Name: within-sdk
Version: 0.1.4
Summary: Within SDK for Python MCP servers — passive usage capture with local subject hashing (FastMCP and low-level MCP).
Project-URL: Homepage, https://getwith.in
Project-URL: Documentation, https://apidocs.getwith.in/
Author: With.in
License: MIT License
        
        Copyright (c) 2024 AgentCat, Inc.
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: analytics,fastmcp,mcp,model-context-protocol
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.11
Requires-Dist: certifi>=2024.0.0
Requires-Dist: mcp<3.0,>=1.2.0
Requires-Dist: pydantic>=2.0
Provides-Extra: community
Requires-Dist: fastmcp!=2.9.*,>=2.7.0; extra == 'community'
Provides-Extra: dev
Requires-Dist: fastmcp>=2.7.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

# Within SDK (Python)

Instrument Model Context Protocol (MCP) servers with privacy-conscious workflow
analytics. Within captures MCP activity, builds a catalog of available tools,
groups pseudonymous user journeys, creates SDK leads, and connects CRM outcomes
back to the workflows that produced them.

[Documentation](https://apidocs.getwith.in) ·
[Quickstart](https://apidocs.getwith.in/quickstart) ·
[API Reference](https://apidocs.getwith.in/sdk/api-reference) ·
[Get an API key](https://app.getwith.in/signup)

## Installation

```bash
pip install within-sdk
```

The SDK requires Python 3.11 or later and supports official MCP servers
(`mcp` 1.2+ and 2.x — both `FastMCP` and `MCPServer`), community FastMCP
v2 servers (`pip install "within-sdk[community]"`), and low-level `mcp`
servers.

## Environment

Configure the vendor slug created during onboarding and its SDK API key in the
server process that runs your MCP server:

```bash
WITHIN_VENDOR_SLUG=acme
WITHIN_SDK_API_KEY=within_sk_xxx
```

Keep the API key in server-side environment or secret storage. Do not expose it
in client code or commit it to source control.

## Quickstart

Register your MCP tools first, then call `track()` on the server instance used
by the running process.

```python
import os

from mcp.server import MCPServer  # mcp 1.x: from mcp.server.fastmcp import FastMCP
from within_sdk import track, WithinOptions, UserIdentity

server = MCPServer("acme-mcp")


@server.tool()
def search_companies(query: str) -> str:
    return do_search(query)


def identify(request, context):
    user = lookup_user_from_request(context)  # e.g. claims off context.request
    if not user:
        return None
    return UserIdentity(
        user_id=user.internal_customer_id,
        user_data={"plan": user.plan, "segment": user.segment},
    )


track(server, os.environ["WITHIN_VENDOR_SLUG"], WithinOptions(
    # api_key falls back to WITHIN_SDK_API_KEY — no need to pass it here
    identify=identify,
))
```

Use a stable, opaque vendor-local ID for `user_id` rather than an email
address, name, or organization domain. The SDK hashes it locally with the
vendor slug (SHA-256, byte-identical to the TypeScript SDK and the outcomes
connector) before SDK activity leaves your process.

By default, `track()` instruments MCP initialization, `tools/list`, and
`tools/call` activity. It also enables tool-call context capture and registers
the `get_more_tools` feedback tool. This changes the advertised tool list and
input schemas, while preserving vendor handler arguments and tool results.

## Public APIs

### `track()`

```python
track(server, vendor_slug, options=None) -> server
```

Instruments an official `MCPServer`/`FastMCP`, community FastMCP, or
compatible low-level MCP server and returns the same server instance. Call it
once for each server instance after registering tools. `track()` never raises
into the host server — configuration problems disable analytics and log a
warning.

### `publish_custom_event()`

Publish a vendor-defined workflow event associated with a tracked server:

```python
from within_sdk import publish_custom_event, CustomEventData

publish_custom_event(server, "acme", CustomEventData(
    session_id=mcp_session_id,
    user_id=user.internal_customer_id,
    resource_name="checkout_started",
    parameters={"plan": "pro"},
    message="User started checkout after an MCP workflow",
    tags={"channel": "mcp"},
))
```

When passed a tracked server, the function reuses its SDK API key and Within
API origin. Pass `session_id` to correlate with an MCP journey and `user_id`
when identity is known. Without either, the event starts a fresh anonymous
journey. You can also pass an MCP session ID string as the first argument from
a process that never called `track()` — provide `api_key` in the event data.

### `report_conversion()`

Report a confirmed subscription conversion from trusted server-side checkout,
webhook, or account-upgrade code:

```python
from within_sdk import report_conversion

result = report_conversion(
    os.environ["WITHIN_VENDOR_SLUG"],
    os.environ["WITHIN_SDK_API_KEY"],
    user.internal_customer_id,
    converted_at=datetime.now(timezone.utc),
    plan={"id": "pro", "name": "Pro", "interval": "month"},
    metadata={"source": "checkout_webhook"},
)
```

Only the user id is required. Use the same opaque vendor-local ID returned by
`identify()`; the SDK creates the subject locally before sending. Repeated
reports for the same subject on the same UTC day return `inserted: False`.

### `get_subject_for_user_id()`

Derive the same vendor-scoped subject used by identification and conversion
reporting. The operation is local and deterministic, which makes it useful for
tests and local verification.

```python
from within_sdk import get_subject_for_user_id

subject = get_subject_for_user_id("acme", user.internal_customer_id)
```

## Configuration

`track(server, vendor_slug, options)` accepts these commonly used options on
`WithinOptions`:

| Option | Purpose |
| --- | --- |
| `api_key` | Within SDK API key. Falls back to `WITHIN_SDK_API_KEY`, then `WITHIN_SDK_INGEST_KEY`. |
| `api_base_url` | Override the Within API origin. The SDK appends `/v1/ingest/*`; most integrations should use the default. |
| `identify` | Resolve a stable vendor-local `user_id` and optional redacted traits for the current request. |
| `log` | Replace the default local SDK log destination with a callback. |
| `enable_tracing` | Capture supported MCP activity. Defaults to `True`. |
| `enable_tool_call_context` | Add and capture the tool-call `context` parameter. Defaults to `True`. |
| `custom_context_description` | Replace the default description shown for the injected `context` parameter. |
| `enable_report_missing` | Register the `get_more_tools` feedback tool. Defaults to `True`. |
| `event_tags` | Add validated string tags to captured activity. |
| `event_properties` | Add custom properties to captured activity. |
| `redact_sensitive_information` | Apply an additional vendor-provided redaction function. |
| `privacy` | `PrivacyOptions` — configure field/event byte limits and additional keys to redact. |

`api_base_url` is an origin such as `https://api.getwith.in`. It falls back to
`WITHIN_SDK_API_URL`, then `WITHIN_SDK_INGEST_BASE_URL`, and finally
`https://api.getwith.in`.
See the [API Reference](https://apidocs.getwith.in/sdk/api-reference) for the
complete option and result types.

## Privacy and redaction

- `user_id` values supplied to identification, custom events, and conversions
  are hashed locally into subjects.
- Raw `user_id` and `user_name` values are not sent to Within.
- Identity-like fields in user data, parameters, responses, tags, properties,
  and conversion metadata are removed or redacted before sending.
- Configurable field and event limits truncate oversized payloads.
- The Within API applies an additional server-side Presidio redaction pass
  for recognized values such as emails, phone numbers, SSNs, card-like values,
  URLs, IPs, bearer tokens, API keys, and secrets.

Pattern-based redaction cannot guarantee detection of every possible name,
location, or sensitive value. Send only data needed for workflow analytics and
use opaque identifiers whenever possible.

## Documentation

- [Within SDK documentation](https://apidocs.getwith.in)
- [Quickstart](https://apidocs.getwith.in/quickstart)
- [API Reference](https://apidocs.getwith.in/sdk/api-reference)
- [Configuration](https://apidocs.getwith.in/sdk/configuration)
- [Privacy and redaction](https://apidocs.getwith.in/sdk/privacy-redaction)
- [Troubleshooting](https://apidocs.getwith.in/troubleshooting)

The Within dashboard presents this data as **SDK Activity**, **SDK Leads**,
**Session Replay**, **Agent Journey Map**, **Context Explorer**, and
**Missing Tool Demand**.

## Onboarding: Sign up and get an API key

1. [Create a Within dashboard account](https://app.getwith.in/signup)
   using your work email.
2. Enter the verification code sent to your email, then sign in to the Within
   dashboard and create a vendor.
3. Open **Settings → SDK Setup**, select the vendor, and generate its
   **SDK API key**.
4. Copy the newly displayed key and store it securely. The dashboard does not
   retain the plaintext key for later display.
5. Set the key as `WITHIN_SDK_API_KEY`, set your registered slug as
   `WITHIN_VENDOR_SLUG`, and use both values in `track()`.

---

Forked from [mcpcat-python-sdk](https://github.com/mcpcat/mcpcat-python-sdk) (MIT).
