# Port of Context (pctx) — Full Documentation > Port of Context (pctx) is the self-hosted, model-agnostic platform for > deploying, running, and observing AI agents in production. Its open-source > core runs an agent's tool calls as generated, type-checked code in isolated, > contained Deno runtimes — replacing sequential LLM tool-calling with > single-pass code execution that uses up to 98% fewer tokens. Self-hosted, > MIT-licensed, works with any model. pctx is NOT an LLM, NOT an agent framework, and NOT a hosted service. It is the execution and observability layer your agents run on, inside your own boundary — your cloud, on-prem, or fully air-gapped. The core is Rust with an isolated Deno-based runtime. The Python SDK (pctx-client) is an HTTP client to the pctx server. - Latest: pctx core v0.7.1; Python client (pctx-client) v0.4.0 - Install via: npm (@portofcontext/pctx), Homebrew, or curl - SDKs: Python (pctx-client), TypeScript - Compatible with: Claude, GPT, Gemini, and local / open-weight models - Any existing MCP server works unchanged - Auth secrets never reach the LLM - Observability built on AVP, an open agent-observability spec --- # README The open source framework to connect AI agents to tools and MCP with Code Mode. ## Install ```bash # Homebrew brew install portofcontext/tap/pctx # cURL curl --proto '=https' --tlsv1.2 -LsSf https://raw.githubusercontent.com/portofcontext/pctx/main/install.sh | sh # npm npm i -g @portofcontext/pctx ``` ## Core Functionality pctx can be run as a stateless HTTP server for Code Mode sessions or as a unified MCP server that exposes Code Mode functionality for registered upstream MCP servers. ```bash # Start Code Mode for Python SDK pctx start # Start Code Mode as a unified MCP server pctx mcp init pctx mcp dev ``` ## Python SDK Use the Python SDK if building agents in Python and want to run Code Mode with custom tools and/or MCP servers. The Python SDK is an HTTP client to the `pctx` server. ```bash pip install pctx-client ``` ```python from pctx_client import Pctx, tool from agents import Agent # Use any Agent SDK from agents.run import Runner # This example is OpenAI Agents SDK @tool def get_weather(city: str) -> str: """Get weather information for a given city.""" return f"It's always sunny in {city}!" pctx = Pctx(tools=[get_weather]) # or with mcp: servers=[your_mcp] tools = pctx.openai_agents_tools() # Run Code Mode with any Agent SDK agent = Agent( name="GreatCoder", model="litellm/openrouter/openai/gpt-oss-120b", instructions="You run code to complete complex tasks.", tools=tools, ) ``` ## Unified MCP Use the unified MCP to run Code Mode with MCP servers and want to persist the authentication connections and you do not need to use agent tools (non-mcp tools). ```bash # Initialize config for upstream mcp connections pctx mcp init # Add HTTP or stdio MCP servers pctx mcp add stripe https://mcp.stripe.com pctx mcp add memory --command "npx -y @modelcontextprotocol/server-memory" # Start as HTTP server (dev mode with UI) pctx mcp dev # Or start as stdio MCP server pctx mcp start --stdio ``` ## What is pctx? `pctx` sits between AI agents and MCP servers. It aggregates multiple upstream MCP servers, handles authentication, and exposes tools through a unified Code Mode interface. Instead of agents managing connections to individual MCP servers, they connect once to pctx. ## What is Code Mode? Code mode replaces sequential tool calling with code execution. Rather than an agent calling tools one at a time and passing results through its context window, it writes code that executes in an isolated, contained runtime. Read Anthropic's overview [here](https://www.anthropic.com/engineering/code-execution-with-mcp). **Traditional MCP flow**: 1. Agent calls `getSheet(id)` 2. Server returns 1000 rows → agent's context 3. Agent calls `filterRows(criteria)` 4. Server returns 50 rows → agent's context **With Code Mode**: ```typescript const sheet = await gdrive.getSheet({ sheetId: "abc" }); const orders = sheet.filter((row) => row.status === "pending"); console.log(`Found ${orders.length} orders`); ``` Result: 98.7% reduction in tokens (150k → 2k) for this multi-step operation. ## Features - **Code Mode interface**: Tools exposed as code functions for efficient agent interaction. - **Upstream MCP server aggregation**: Connect to multiple MCP servers through a single interface, with persistent upstream sessions across `execute_typescript` calls. - **Simple config with CLI**: Create the pctx.json config with a simple CLI. pctx.json manages auth, upstream MCPs, logging, and more. - **Secure authentication**: Source secrets from environment variables, system keychain, and external commands. ## Security - LLM generated code runs in an isolated, contained Deno runtime that can only access the network hosts specified in the configuration file. - No filesystem, environment, network (beyond allowed hosts), or system access. - MCP clients are authenticated in pctx. LLMs can never see your auth. ## Update ```bash # Homebrew update brew upgrade pctx # cURL update pctx-update # npm update npm upgrade -g @portofcontext/pctx ``` --- # Code Mode Interface **`pctx` is a bring-your-own-LLM proxy** that exposes MCP tools as TypeScript functions for AI agents. ## What is Code Mode? Instead of sequential tool calls passing data through the model's context window, code mode lets AI agents write TypeScript that executes in an isolated, contained environment. **Traditional MCP:** Each tool call → model context → high token usage **Code Mode:** Write TypeScript → execute locally → 98% fewer tokens ```typescript // Data stays in execution environment, process with native JS const items = await myserver.getItems(); const filtered = items.filter((x) => x.status === "pending"); await myserver.updateItems(filtered.map((x) => ({ ...x, processed: true }))); ``` ## How It Works ``` 1. AI discovers → pctx.list_functions() 2. AI gets details → pctx.get_function_details(['gdrive.getSheet']) 3. AI writes code → TypeScript using discovered functions 4. pctx type-checks → Instant feedback (< 100ms) 5. pctx executes → isolated Deno runtime if types pass 6. AI gets results → stdout + return value ``` ### Type Checking Before Execution `pctx` validates TypeScript **before running code** using `typescript-go`: ```typescript // ✓ Valid await gdrive.getSheet({ sheetId: "abc123" }); // ✗ Type errors caught instantly await gdrive.getSheet({ sheetId: 123 }); // Error: Type 'number' is not assignable to type 'string' ``` **10-20x faster iteration** - No execution overhead for type errors. ### Isolated Execution Code runs in Deno with strict limits: - **10-second timeout** - **No filesystem/env access** - **Network restricted** to configured MCP hosts only - **Pre-authenticated** MCP clients (AI never sees credentials) ## MCP Tools & Tool Disclosure `pctx` exposes tools that your LLM calls. The exact set depends on the `disclosure` mode configured for your server. ### `catalog` mode (default) Three tools for dynamic discovery and execution. | Tool | Description | | ---------------------- | ------------------------------------------------------------------------------------------- | | `list_functions` | Returns TypeScript namespaces for all connected MCP servers | | `get_function_details` | Returns full TypeScript signatures with JSDoc for specific functions | | `execute_typescript` | Runs TypeScript code with type checking, returns `{ success, stdout, output, diagnostics }` | ``` list_functions() → get_function_details([...]) → execute_typescript({ code }) ``` ### `filesystem` mode Two tools for dynamic discovery and execution. The generated TypeScript code is loaded into a virtual filesystem that LLMs can explore (`grep`, `find`, `cat`, `sed`, etc.) to gain the knowledge to write a script. | Tool | Description | | -------------------- | ----------------------------------------------------- | | `execute_bash` | Reads TypeScript tool definitions from the filesystem | | `execute_typescript` | Runs TypeScript code with type checking | ``` execute_bash() → execute_typescript({ code }) ``` ### `sidecar` mode _Currently only supported in the unified MCP server with `pctx mcp start`_ Upstream tool descriptions are surfaced directly as MCP tools with the addition of `execute_typescript`; the agent calls `execute_typescript` to invoke them without a separate discovery step. ## Namespaces Each MCP server becomes a TypeScript namespace: ```typescript // Server names from config await gdrive.getSheet({ sheetId: "abc" }); await slack.sendMessage({ channel: "#general", text: "hi" }); ``` ## Example ```typescript // Traditional: 50K+ tokens for multiple tool calls // Code mode: Single execution, data stays in the isolated runtime const orders = await store.getOrders(); const pending = orders.filter((o) => o.status === "pending"); const total = pending.reduce((sum, o) => sum + o.amount, 0); console.log(`${pending.length} pending orders: $${total}`); ``` --- # CLI Reference This document contains the help content for the `pctx` command-line program. ## `pctx` Use pctx to expose code mode either as a session based server or by aggregating multiple MCP servers into a single code mode MCP server. **Usage:** `pctx [OPTIONS] ` EXAMPLES: # Code Mode sessions pctx start # Code Mode MCP pctx mcp init pctx mcp add my-server https://mcp.example.com pctx mcp dev **Subcommands:** * `start` — Start pctx server for code mode sessions * `mcp` — MCP server commands (with pctx.json configuration) **Options:** * `-c`, `--config ` — Config file path, defaults to ./pctx.json (Default: `pctx.json`) * `-q`, `--quiet` — No logging except for errors * `-v`, `--verbose` — Verbose logging (-v) or trace logging (-vv) ## `pctx start` Starts pctx server with no pre-configured tools. Use a client library like `pip install pctx-client` to create sessions, register tools, and expose code-mode tools to agent libraries. **Usage:** `pctx start [OPTIONS]` **Options:** * `-p`, `--port ` — Port to listen on (Default: `8080`) * `--host ` — Host address to bind to (use 0.0.0.0 for external access) (Default: `127.0.0.1`) * `--session-dir ` — Path to session storage directory (Default: `.pctx/sessions`) * `--allowed-origin ` — Allowed CORS origins. Can be specified multiple times. * `--no-banner` — Don't show the server banner ## `pctx mcp` MCP server commands (with pctx.json configuration) **Usage:** `pctx mcp ` **Subcommands:** * `init` — Initialize pctx.json configuration file * `list` — List MCP servers and test connections * `add` — Add an MCP server to configuration (HTTP or stdio) * `remove` — Remove an MCP server from configuration * `start` — Start the pctx MCP server * `dev` — Start the pctx MCP server with terminal UI ## `pctx mcp init` Initialize pctx.json configuration file. **Usage:** `pctx mcp init [OPTIONS]` **Options:** * `-y`, `--yes` — Use default values and skip interactive adding of upstream MCPs ## `pctx mcp list` Lists configured MCP servers and tests the connection to each. **Usage:** `pctx mcp list` ## `pctx mcp add` Add a new MCP server to the configuration. Supports both HTTP(S) URLs and stdio-based servers via the --command flag. **Usage:** `pctx mcp add [OPTIONS] [URL]` **Arguments:** * `` — Unique name for this server * `` — HTTP(S) URL of the MCP server endpoint (conflicts with --command for stdio) **Options:** * `--command ` — Command to execute for stdio MCP server (conflicts with url) * `--arg ` — Arguments to pass to the stdio command (repeat for multiple) * `--env ` — Environment variables in KEY=VALUE format (repeat for multiple) * `-b`, `--bearer ` — use bearer authentication using PCTX's secret string syntax. e.g. `--bearer '${env:BEARER_TOKEN}'` * `-H`, `--header
` — use custom headers using PCTX's secret string syntax. e.g. `--headers 'x-api-key: ${keychain:API_KEY}'` * `-f`, `--force` — Overrides any existing server under the same name & skips testing connection ## `pctx mcp remove` Remove an MCP server from the configuration. **Usage:** `pctx mcp remove ` **Arguments:** * `` — Name of the server to remove ## `pctx mcp start` Start the pctx MCP server (exposes /mcp endpoint). **Usage:** `pctx mcp start [OPTIONS]` **Options:** * `-p`, `--port ` — Port to listen on (Default: `8080`) * `--host ` — Host address to bind to (Default: `127.0.0.1`) * `--no-banner` — Don't show the server banner * `--stdio` — Serve MCP over stdio instead of HTTP * `--stateful-http` — Use stateful MCP sessions (incompatible with --stdio) ## `pctx mcp dev` Start the pctx MCP server in development mode with an interactive terminal UI. **Usage:** `pctx mcp dev [OPTIONS]` **Options:** * `-p`, `--port ` — Port to listen on (Default: `8080`) * `--host ` — Host address to bind to (Default: `127.0.0.1`) * `--log-file ` — Path to JSONL log file (Default: `pctx-dev.jsonl`) * `--stdio` — Serve MCP over stdio instead of HTTP * `--stateful-http` — Use stateful HTTP sessions (incompatible with --stdio) --- # Configuration Guide The `pctx.json` file defines your MCP server aggregation, authentication, and runtime configuration. ## File Location By default, `pctx` looks for `./pctx.json` in the current working directory. Override with `--config`: ```bash pctx --config /path/to/config.json start ``` ## Quick Start ```bash pctx init ``` This creates a basic `pctx.json` and prompts you to add upstream MCP servers. ## Root Fields | Field | Type | Required | Default | Description | | ------------- | --------------------- | -------- | ----------- | ------------------------------------------------------ | | `name` | `string` | Yes | - | Name of your MCP server instance | | `version` | `string` | Yes | `"0.1.0"` | Version of your MCP server | | `description` | `string` | No | - | Optional description of your MCP server | | `disclosure` | `ToolDisclosure` | No | `"catalog"` | Tool disclosure mode | | `servers` | `array[ServerConfig]` | Yes | - | List of upstream MCP server configurations | | `logger` | `LoggerConfig` | No | - | Logger configuration | | `telemetry` | `TelemetryConfig` | No | - | OpenTelemetry configuration | ## Tool Disclosure The `disclosure` field controls which set of code-mode tools are exposed to the AI agent. | Value | Default | Description | | -------------- | ------- | ------------------------------------------------------------------------------------------------- | | `"catalog"` | Yes | Agent uses `list_tools` → `get_tool_details` → `execute_typescript` to discover and call tools | | `"filesystem"` | No | Agent uses `execute_bash` → `execute_typescript`; tool details are read from the filesystem | | `"sidecar"` | No | Upstream tool descriptions are surfaced directly; agent calls `execute_typescript` to invoke them | ## Server Configuration Each server in the `servers` array is either an HTTP server or a stdio server. **HTTP server fields:** | Field | Type | Required | Description | | ------ | ------------ | -------- | ---------------------------------------------- | | `name` | `string` | Yes | Unique identifier used as TypeScript namespace | | `url` | `string` | Yes | HTTP(S) URL of the MCP server endpoint | | `auth` | `AuthConfig` | No | Authentication configuration | **Stdio server fields:** | Field | Type | Required | Description | | --------- | ------------------- | -------- | -------------------------------------------------------------------------------------------------- | | `name` | `string` | Yes | Unique identifier used as TypeScript namespace | | `command` | `string` | Yes | Command to execute the MCP server | | `args` | `array[string]` | No | Arguments passed to the command | | `env` | `map[string]string` | No | Environment variables for the process | **Examples:** ```json { "name": "memory", "command": "npx -y @modelcontextprotocol/server-memory" } ``` ```json { "name": "local_tools", "command": "node", "args": ["./dist/server.js"], "env": { "NODE_ENV": "development" } } ``` ### Server Names as Namespaces The `name` will be case converted to `camelCase` and used as the TypeScript namespace: ```typescript // Server name: "g_drive" await gDrive.getSheet({ sheetId: "abc" }); // Server name: "slack" await slack.sendMessage({ channel: "#general", text: "hi" }); ``` ## Authentication The `auth` field supports two types: `BearerToken | Custom`. ### Bearer Token ```json { "type": "bearer", "token": "${env:API_TOKEN}" } ``` Adds `Authorization: Bearer ` header to all requests. ### Header Authentication ```json { "type": "headers", "headers": { "x-api-key": "${env:API_KEY}", "x-custom-header": "static-value" } } ``` ## Secret String Syntax Both `token` and header values support a secret string syntax for secure credential management. ### Environment Variables **Format:** `${env:VARIABLE_NAME}` ```json { "token": "${env:MCP_API_TOKEN}" } ``` ### System Keychain **Format:** `${keychain:KEY_NAME}` ```json { "token": "${keychain:mcp-api-key}" } ``` Reads from your OS keychain (macOS Keychain, Windows Credential Manager, Linux Secret Service). ### External Commands **Format:** `${command:shell command}` ```json { "token": "${command:aws secretsmanager get-secret-value --secret-id my-token --query SecretString --output text}" } ``` ### Combining Plain Text and Secrets ```json { "headers": { "authorization": "ApiKey ${keychain:api-key}", "x-custom": "prefix-${env:SUFFIX}" } } ``` ## Logger Configuration | Field | Type | Required | Default | Description | | --------- | -------------- | -------- | ----------- | -------------------------------------------------- | | `enabled` | `boolean` | No | `true` | Enable or disable logging | | `level` | `LogLevel` | No | `"info"` | Minimum log level: trace, debug, info, warn, error | | `format` | `LoggerFormat` | No | `"compact"` | Output format: compact, pretty, json | | `colors` | `boolean` | No | `true` | Enable or disable colorized output | ## Telemetry Configuration The optional `telemetry` field enables OpenTelemetry (OTLP) integration for distributed tracing and metrics. | Field | Type | Required | Default | Description | | --------- | --------------- | -------- | ------- | --------------------------------- | | `traces` | `TracesConfig` | No | - | Distributed tracing configuration | | `metrics` | `MetricsConfig` | No | - | Metrics collection configuration | Each exporter supports `http` or `grpc` protocol, optional auth (bearer, basic, or headers), and configurable timeout. ## Complete Example ```json { "name": "my-ai-agent", "version": "1.0.0", "description": "MCP server aggregation for my AI agent", "logger": { "enabled": true, "level": "info", "format": "compact", "colors": true }, "servers": [ { "name": "stripe", "url": "https://mcp.stripe.com", "auth": { "type": "bearer", "token": "${env:STRIPE_MCP_KEY}" } }, { "name": "gdrive", "url": "https://mcp.gdrive.example.com", "auth": { "type": "headers", "headers": { "x-api-key": "${keychain:gdrive-api-key}" } } }, { "name": "internal", "url": "https://internal-mcp.company.com", "auth": { "type": "bearer", "token": "${command:vault kv get -field=token secret/mcp}" } }, { "name": "public", "url": "https://public-mcp.example.com" }, { "name": "memory", "command": "npx -y @modelcontextprotocol/server-memory" }, { "name": "local_tools", "command": "node", "args": ["./dist/server.js"], "env": { "NODE_ENV": "production" } } ] } ``` ## Troubleshooting ### "Failed to connect" Error Check: URL is correct, server is running, network/firewall allows the connection. ### "Server requires authentication" Error The server returned 401/403. Add authentication: ```bash pctx add my-server https://mcp.example.com --bearer '${env:TOKEN}' ``` ### "Environment variable not found" Error ```bash cat pctx.json | grep env: export API_TOKEN="your-token" ``` ### "Failed to retrieve password from keychain" Error Create the keychain entry: ```bash # macOS security add-generic-password -s pctx -a my-key -w "my-value" ``` --- # Upstream MCP Servers Connect multiple MCP servers through a single interface with unified authentication. ## Overview PCTX aggregates multiple MCP servers into a single endpoint, allowing AI agents to interact with many services through one interface. ``` AI Agent │ PCTX (localhost:8080) ├─ Google Drive MCP ├─ Slack MCP ├─ GitHub MCP └─ Custom Internal MCP ``` Instead of configuring each MCP server separately in your AI tool, configure pctx once. ## How It Works ### 1. Server Registration Each server is registered with a unique name. The name becomes the TypeScript namespace for that server's tools. ### 2. Tool Aggregation When pctx starts, it: 1. Connects to each configured server 2. Fetches tool definitions from each 3. Generates TypeScript namespaces 4. Exposes all tools through a single endpoint ### 3. Namespace Organization ```typescript // Google Drive tools await gdrive.getSheet({ sheetId: "abc" }); await gdrive.createDocument({ title: "Report" }); // Slack tools await slack.sendMessage({ channel: "#general", text: "hi" }); await slack.getUsers(); // Internal tools await internal.processOrder({ orderId: "123" }); await internal.sendNotification({ type: "email" }); ``` ## Upstream Sessions Some upstream MCP servers are stateful — they maintain internal state across multiple tool calls. pctx preserves upstream connections across `execute_typescript` calls using a connection pool. ### `pctx start` (session server) Upstream connections are scoped to a **code mode session**. - First `execute_typescript` creates a connection pool and connects to all configured upstream servers. - Subsequent executions within the same session reuse those connections. - When the session is deleted, all upstream connections are shut down cleanly. Each active session has its own isolated pool; two concurrent sessions never share upstream connections. ### `pctx mcp start` (unified MCP server) Supports three modes with different session scoping: #### HTTP (default — stateless) ```bash pctx mcp start ``` Each request gets a fresh connection pool. Use when upstream MCPs are stateless. #### HTTP with stateful sessions (`--stateful-http`) ```bash pctx mcp start --stateful-http ``` Upstream connections are scoped to an **HTTP session**, identified by the `mcp-session-id` header. #### Stdio (`--stdio`) ```bash pctx mcp start --stdio ``` The entire process lifetime is treated as a single session. A global session ID is assigned at startup, and all `execute_typescript` calls share one connection pool for the life of the process. --- # Python SDK (pctx-client) Python client for using Code Mode via pctx — allow agents to execute code with your custom tools and MCP servers. Full API reference: https://pctx.readthedocs.io/en/latest/ ## Installation ```bash pip install pctx-client ``` ## Quick Start 1. Install pctx server (npm, Homebrew, or curl — see above) 2. Install Python client with agent framework extra: ```bash pip install pctx-client[langchain] langchain langchain_openai ``` 3. Start the Code Mode server: ```bash pctx start ``` 4. Define and run your agent: ```python import asyncio import os from pctx_client import Pctx, tool from langchain.agents import create_agent from langchain_openai import ChatOpenAI @tool def get_weather(city: str) -> str: """Get weather for a given city.""" return f"It's always sunny in {city}!" @tool def get_time(city: str) -> str: """Get time for a given city.""" return f"It is midnight in {city}!" async def main(api_key: str): p = Pctx(tools=[get_weather, get_time]) llm = ChatOpenAI( model="deepseek/deepseek-chat", temperature=0, api_key=api_key, base_url="https://openrouter.ai/api/v1", max_retries=2, ) agent = create_agent( llm, tools=p.langchain_tools(), system_prompt="You are a helpful assistant", ) await p.connect() result = await agent.ainvoke( {"messages": [{"role": "user", "content": "what is the weather and time in nyc"}]} ) await p.disconnect() if __name__ == "__main__": api_key = os.getenv("OPENROUTER_API_KEY") asyncio.run(main(api_key)) ``` ## Code Mode Code Mode allows AI agents to execute TypeScript code with access to both your custom Python tools and MCP servers. All available `Pctx` code mode functions: 1. **`list_functions()`** - Lists all available functions organized by namespace. 2. **`get_function_details(functions)`** - Returns detailed function info including parameter types, return values. 3. **`search_functions(query, top_k)`** - Requires `pctx-client[bm25s]`. BM25s vector search over available functions. 4. **`execute_bash(cmd)`** - Executes bash in a virtual filesystem containing the generated TypeScript code. 5. **`execute_typescript(code)`** - Executes TypeScript in an isolated Deno runtime. ### ToolDisclosure Controls which code-mode tools are exposed to the agent: | Value | Tools exposed | | ------------------------------------ | ----------------------------------------------------------------------------------------------------------- | | `ToolDisclosure.CATALOG` _(default)_ | `list_functions`, `get_function_details`, `execute_typescript` (+ `search_functions` if bm25s is installed) | | `ToolDisclosure.FS` | `execute_bash`, `execute_typescript` | ## Defining Tools ### Decorator Approach ```python from pctx_client import tool @tool def get_weather(city: str) -> str: """Get weather information for a given city.""" return f"It's always sunny in {city}!" pctx = Pctx(tools=[get_weather]) ``` #### Custom Name and Namespace ```python @tool(name="weather_lookup", namespace="weather_api", description="Fetches current weather") def fetch_weather(location: str) -> str: return f"Weather for {location}: Sunny, 72°F" ``` #### Async Tools ```python @tool async def fetch_user_data(user_id: int) -> dict[str, str]: """Asynchronously fetch user data from an API.""" await asyncio.sleep(0.1) return {"id": str(user_id), "name": "John Doe"} ``` #### Nested Types with Pydantic ```python from pydantic import BaseModel, Field from typing import List, Optional class Address(BaseModel): street: str city: str zip_code: str = Field(description="5-digit ZIP code") country: str = "USA" class UserProfile(BaseModel): name: str age: int email: str addresses: List[Address] preferences: Optional[dict[str, bool]] = None class UpdateResult(BaseModel): success: bool user_id: str updated_fields: List[str] message: str @tool def update_user_profile(user_id: str, profile: UserProfile, notify: bool = True) -> UpdateResult: """Update a user's profile with complex nested data.""" return UpdateResult( success=True, user_id=user_id, updated_fields=["name", "age", "email", "addresses"], message=f"Successfully updated profile for user {user_id}" ) ``` ### Class-Based Approach Subclass `Tool` (synchronous) or `AsyncTool` (asynchronous) and implement `_invoke` or `_ainvoke`. You **MUST** define `input_schema` and `output_schema` attributes. ```python from pctx_client import Tool from pydantic import BaseModel from typing import Literal class CalculatorInput(BaseModel): operation: Literal["add", "subtract", "multiply", "divide"] x: float y: float class Calculator(Tool): name: str = "calculator" namespace: str = "math" description: str = "Performs basic arithmetic operations" input_schema: type[BaseModel] = CalculatorInput output_schema: type[float] = float def _invoke(self, operation: Literal["add", "subtract", "multiply", "divide"], x: float, y: float) -> float: if operation == "add": return x + y elif operation == "subtract": return x - y elif operation == "multiply": return x * y elif operation == "divide": return x / y pctx = Pctx(tools=[Calculator()]) ``` ### Registering Tools ```python from pctx_client import Pctx # Mix decorator and class-based tools p = Pctx(tools=[get_weather, Calculator(), WebSearchTool()]) ``` ## Registering MCP Servers ### HTTP MCP Servers ```python servers = [ {"name": "weather", "url": "http://localhost:3000/mcp"}, {"name": "api", "url": "https://api.example.com/mcp", "auth": {"type": "bearer", "token": "your-token"}} ] p = Pctx(servers=servers) ``` ### Stdio MCP Servers ```python servers = [ {"name": "local-mcp", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-everything"]} ] p = Pctx(servers=servers) ``` ### Combining Tools and Servers ```python @tool def custom_function(input: str) -> str: """A custom local function.""" return f"Processed: {input}" servers = [ {"name": "api", "url": "https://api.example.com/mcp"}, {"name": "local", "command": "node", "args": ["./server.js"]} ] p = Pctx(tools=[custom_function], servers=servers) ``` ## Agent Frameworks Install the appropriate extra for your framework: ```bash pip install pctx-client[langchain] # LangChain pip install pctx-client[claude] # Claude Agent SDK pip install pctx-client[crewai] # CrewAI pip install pctx-client[openai] # OpenAI Agents SDK pip install pctx-client[pydantic-ai] # Pydantic AI ``` pctx can be integrated into any agent framework by wrapping the 3 Code Mode tools available on the `Pctx` class. --- For quick reference, see [llms.txt](https://portofcontext.com/llms.txt) Source: https://github.com/portofcontext/pctx