updated readme, env var names
This commit is contained in:
parent
f4e5a2d03b
commit
028a62f918
4 changed files with 228 additions and 81 deletions
245
README.md
245
README.md
|
|
@ -9,7 +9,7 @@ FastMCP. Streaming and webhook endpoints are excluded.
|
||||||
- An X Developer Platform app (to get tokens)
|
- An X Developer Platform app (to get tokens)
|
||||||
- Optional: an xAI API key if you want to run the Grok test client
|
- Optional: an xAI API key if you want to run the Grok test client
|
||||||
|
|
||||||
## Quick start (local server)
|
## Setup (local)
|
||||||
|
|
||||||
1. Create a virtual environment and install dependencies:
|
1. Create a virtual environment and install dependencies:
|
||||||
- `python -m venv .venv`
|
- `python -m venv .venv`
|
||||||
|
|
@ -17,97 +17,218 @@ FastMCP. Streaming and webhook endpoints are excluded.
|
||||||
- `pip install -r requirements.txt`
|
- `pip install -r requirements.txt`
|
||||||
2. Create your local `.env`:
|
2. Create your local `.env`:
|
||||||
- `cp env.example .env`
|
- `cp env.example .env`
|
||||||
- Fill in the OAuth1 section (consumer key/secret and callback settings).
|
- Required values (do not skip):
|
||||||
3. Run the server:
|
- `X_OAUTH_CONSUMER_KEY`
|
||||||
- `python server.py`
|
- `X_OAUTH_CONSUMER_SECRET`
|
||||||
|
- `X_BEARER_TOKEN` (required for this setup; keep it set even if using OAuth1)
|
||||||
The server starts at `http://127.0.0.1:8000` by default.
|
- OAuth1 callback (defaults are fine):
|
||||||
The MCP endpoint is `http://127.0.0.1:8000/mcp`.
|
- `X_OAUTH_CALLBACK_HOST` (default `127.0.0.1`)
|
||||||
|
- `X_OAUTH_CALLBACK_PORT` (default `8976`)
|
||||||
## Environment variables
|
- `X_OAUTH_CALLBACK_PATH` (default `/oauth/callback`)
|
||||||
|
- `X_OAUTH_CALLBACK_TIMEOUT` (default `300`)
|
||||||
Required (OAuth1 user context):
|
- Server settings (optional):
|
||||||
- `TWITTER_CONSUMER_KEY`
|
- `X_API_BASE_URL` (default `https://api.x.com`)
|
||||||
- `TWITTER_CONSUMER_SECRET`
|
- `X_API_TIMEOUT` (default `30`)
|
||||||
- `X_OAUTH_CALLBACK_HOST` (default `127.0.0.1`)
|
- `MCP_HOST` (default `127.0.0.1`)
|
||||||
- `X_OAUTH_CALLBACK_PORT` (default `8976`)
|
- `MCP_PORT` (default `8000`)
|
||||||
- `X_OAUTH_CALLBACK_PATH` (default `/oauth/callback`)
|
- `X_API_DEBUG` (default `1`)
|
||||||
- `X_OAUTH_CALLBACK_TIMEOUT` (default `300`)
|
- Tool filtering (optional, comma-separated):
|
||||||
|
- `X_API_TOOL_ALLOWLIST`
|
||||||
Optional auth fallback:
|
- Optional Grok test client:
|
||||||
- `X_BEARER_TOKEN` (OAuth2 bearer token)
|
- `XAI_API_KEY`
|
||||||
|
- `XAI_MODEL` (default `grok-4-1-fast`)
|
||||||
Optional server config:
|
- `MCP_SERVER_URL` (default `http://127.0.0.1:8000/mcp`)
|
||||||
- `MCP_HOST` (default `127.0.0.1`)
|
- Optional OAuth2 token generation:
|
||||||
- `MCP_PORT` (default `8000`)
|
- `CLIENT_ID`
|
||||||
- `X_API_BASE_URL` (default `https://api.x.com`)
|
- `CLIENT_SECRET`
|
||||||
- `X_API_TIMEOUT` (default `30`)
|
- `X_OAUTH_ACCESS_TOKEN`
|
||||||
- `X_API_DEBUG` (default `1`)
|
- `X_OAUTH_ACCESS_TOKEN_SECRET` (optional)
|
||||||
- `FASTMCP_EXPERIMENTAL_ENABLE_NEW_OPENAPI_PARSER`
|
- Optional OAuth1 debug output:
|
||||||
|
- `X_OAUTH_PRINT_TOKENS`
|
||||||
Tool filtering (comma-separated):
|
- `X_OAUTH_PRINT_AUTH_HEADER`
|
||||||
- `X_API_TOOL_TAGS`
|
3. Register the callback URL in your X Developer App:
|
||||||
- `X_API_TOOL_ALLOWLIST`
|
|
||||||
- `X_API_TOOL_DENYLIST`
|
|
||||||
|
|
||||||
Optional Grok test client:
|
|
||||||
- `XAI_API_KEY`
|
|
||||||
- `XAI_MODEL` (default `grok-4-1-fast`)
|
|
||||||
- `MCP_SERVER_URL` (default `http://127.0.0.1:8000/mcp`)
|
|
||||||
|
|
||||||
## Auth flow (OAuth1 on startup)
|
|
||||||
|
|
||||||
The server runs an OAuth1 browser flow on startup and uses the resulting
|
|
||||||
access token to sign every request. You must register a callback URL in your
|
|
||||||
X Developer App that matches:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
http://<X_OAUTH_CALLBACK_HOST>:<X_OAUTH_CALLBACK_PORT><X_OAUTH_CALLBACK_PATH>
|
http://<X_OAUTH_CALLBACK_HOST>:<X_OAUTH_CALLBACK_PORT><X_OAUTH_CALLBACK_PATH>
|
||||||
```
|
```
|
||||||
|
|
||||||
Example:
|
Example (defaults):
|
||||||
|
|
||||||
```
|
```
|
||||||
http://127.0.0.1:8976/oauth/callback
|
http://127.0.0.1:8976/oauth/callback
|
||||||
```
|
```
|
||||||
|
|
||||||
When you start the server, it will open a browser tab for consent and wait
|
4. Start the server:
|
||||||
for the callback. Tokens are kept in memory only for the lifetime of the
|
|
||||||
server process.
|
|
||||||
|
|
||||||
## Tool whitelisting
|
|
||||||
|
|
||||||
If you want to limit the tool list (smaller context window, fewer tools),
|
|
||||||
use `X_API_TOOL_ALLOWLIST` or `X_API_TOOL_TAGS` in `.env`.
|
|
||||||
|
|
||||||
Example allowlist:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
X_API_TOOL_ALLOWLIST=getUsersByUsername,createDirectMessagesByParticipantId
|
python server.py
|
||||||
```
|
```
|
||||||
|
|
||||||
Example tags:
|
The MCP endpoint is `http://127.0.0.1:8000/mcp` by default.
|
||||||
|
|
||||||
|
5. Connect an MCP client:
|
||||||
|
- Local client: point it to `http://127.0.0.1:8000/mcp`.
|
||||||
|
- Remote client: tunnel your local server (e.g., ngrok) and use the public URL.
|
||||||
|
|
||||||
|
## Whitelisting tools
|
||||||
|
|
||||||
|
Use `X_API_TOOL_ALLOWLIST` to load a small, explicit set of tools:
|
||||||
|
|
||||||
```
|
```
|
||||||
X_API_TOOL_TAGS=users,dm
|
X_API_TOOL_ALLOWLIST=getUsersByUsername,createPosts,searchPostsRecent
|
||||||
```
|
```
|
||||||
|
|
||||||
Allowlist and tags are applied at startup when the OpenAPI spec is loaded.
|
Whitelisting is applied at startup when the OpenAPI spec is loaded, so restart
|
||||||
|
the server after changes. See the full tool list below before building your
|
||||||
|
allowlist.
|
||||||
|
|
||||||
|
## OAuth1 flow (startup behavior)
|
||||||
|
|
||||||
|
On startup, the server opens a browser for OAuth1 consent and waits for the
|
||||||
|
callback. Tokens are kept in memory only for the lifetime of the server
|
||||||
|
process. Set `X_OAUTH_PRINT_TOKENS=1` to print tokens, or
|
||||||
|
`X_OAUTH_PRINT_AUTH_HEADER=1` to print request headers.
|
||||||
|
|
||||||
|
## Available tool calls (allowlist-ready)
|
||||||
|
|
||||||
|
Below is the full list of tool calls you can whitelist via
|
||||||
|
`X_API_TOOL_ALLOWLIST`. Copy any of these into your `.env` allowlist.
|
||||||
|
|
||||||
|
- `addListsMember`
|
||||||
|
- `addUserPublicKey`
|
||||||
|
- `appendMediaUpload`
|
||||||
|
- `blockUsersDms`
|
||||||
|
- `createCommunityNotes`
|
||||||
|
- `createComplianceJobs`
|
||||||
|
- `createDirectMessagesByConversationId`
|
||||||
|
- `createDirectMessagesByParticipantId`
|
||||||
|
- `createDirectMessagesConversation`
|
||||||
|
- `createLists`
|
||||||
|
- `createMediaMetadata`
|
||||||
|
- `createMediaSubtitles`
|
||||||
|
- `createPosts`
|
||||||
|
- `createUsersBookmark`
|
||||||
|
- `deleteActivitySubscription`
|
||||||
|
- `deleteAllConnections`
|
||||||
|
- `deleteCommunityNotes`
|
||||||
|
- `deleteConnectionsByEndpoint`
|
||||||
|
- `deleteConnectionsByUuids`
|
||||||
|
- `deleteDirectMessagesEvents`
|
||||||
|
- `deleteLists`
|
||||||
|
- `deleteMediaSubtitles`
|
||||||
|
- `deletePosts`
|
||||||
|
- `deleteUsersBookmark`
|
||||||
|
- `evaluateCommunityNotes`
|
||||||
|
- `finalizeMediaUpload`
|
||||||
|
- `followList`
|
||||||
|
- `followUser`
|
||||||
|
- `getAccountActivitySubscriptionCount`
|
||||||
|
- `getActivitySubscriptions`
|
||||||
|
- `getChatConversation`
|
||||||
|
- `getChatConversations`
|
||||||
|
- `getCommunitiesById`
|
||||||
|
- `getComplianceJobs`
|
||||||
|
- `getComplianceJobsById`
|
||||||
|
- `getConnectionHistory`
|
||||||
|
- `getDirectMessagesEvents`
|
||||||
|
- `getDirectMessagesEventsByConversationId`
|
||||||
|
- `getDirectMessagesEventsById`
|
||||||
|
- `getDirectMessagesEventsByParticipantId`
|
||||||
|
- `getInsights28Hr`
|
||||||
|
- `getInsightsHistorical`
|
||||||
|
- `getListsById`
|
||||||
|
- `getListsFollowers`
|
||||||
|
- `getListsMembers`
|
||||||
|
- `getListsPosts`
|
||||||
|
- `getMarketplaceHandleAvailability`
|
||||||
|
- `getMediaAnalytics`
|
||||||
|
- `getMediaByMediaKey`
|
||||||
|
- `getMediaByMediaKeys`
|
||||||
|
- `getMediaUploadStatus`
|
||||||
|
- `getNews`
|
||||||
|
- `getOpenApiSpec`
|
||||||
|
- `getPostsAnalytics`
|
||||||
|
- `getPostsById`
|
||||||
|
- `getPostsByIds`
|
||||||
|
- `getPostsCountsAll`
|
||||||
|
- `getPostsCountsRecent`
|
||||||
|
- `getPostsLikingUsers`
|
||||||
|
- `getPostsQuotedPosts`
|
||||||
|
- `getPostsRepostedBy`
|
||||||
|
- `getPostsReposts`
|
||||||
|
- `getSpacesBuyers`
|
||||||
|
- `getSpacesByCreatorIds`
|
||||||
|
- `getSpacesById`
|
||||||
|
- `getSpacesByIds`
|
||||||
|
- `getSpacesPosts`
|
||||||
|
- `getTrendsByWoeid`
|
||||||
|
- `getTrendsPersonalizedTrends`
|
||||||
|
- `getUsage`
|
||||||
|
- `getUserPublicKeys`
|
||||||
|
- `getUsersAffiliates`
|
||||||
|
- `getUsersBlocking`
|
||||||
|
- `getUsersBookmarkFolders`
|
||||||
|
- `getUsersBookmarks`
|
||||||
|
- `getUsersBookmarksByFolderId`
|
||||||
|
- `getUsersById`
|
||||||
|
- `getUsersByIds`
|
||||||
|
- `getUsersByUsername`
|
||||||
|
- `getUsersByUsernames`
|
||||||
|
- `getUsersFollowedLists`
|
||||||
|
- `getUsersFollowers`
|
||||||
|
- `getUsersFollowing`
|
||||||
|
- `getUsersLikedPosts`
|
||||||
|
- `getUsersListMemberships`
|
||||||
|
- `getUsersMe`
|
||||||
|
- `getUsersMentions`
|
||||||
|
- `getUsersMuting`
|
||||||
|
- `getUsersOwnedLists`
|
||||||
|
- `getUsersPinnedLists`
|
||||||
|
- `getUsersPosts`
|
||||||
|
- `getUsersRepostsOfMe`
|
||||||
|
- `getUsersTimeline`
|
||||||
|
- `hidePostsReply`
|
||||||
|
- `initializeMediaUpload`
|
||||||
|
- `likePost`
|
||||||
|
- `mediaUpload`
|
||||||
|
- `muteUser`
|
||||||
|
- `pinList`
|
||||||
|
- `removeListsMemberByUserId`
|
||||||
|
- `repostPost`
|
||||||
|
- `searchCommunities`
|
||||||
|
- `searchCommunityNotesWritten`
|
||||||
|
- `searchEligiblePosts`
|
||||||
|
- `searchNews`
|
||||||
|
- `searchPostsAll`
|
||||||
|
- `searchPostsRecent`
|
||||||
|
- `searchSpaces`
|
||||||
|
- `searchUsers`
|
||||||
|
- `sendChatMessage`
|
||||||
|
- `unblockUsersDms`
|
||||||
|
- `unfollowList`
|
||||||
|
- `unfollowUser`
|
||||||
|
- `unlikePost`
|
||||||
|
- `unmuteUser`
|
||||||
|
- `unpinList`
|
||||||
|
- `unrepostPost`
|
||||||
|
- `updateActivitySubscription`
|
||||||
|
- `updateLists`
|
||||||
|
|
||||||
## Generate an OAuth2 user token (optional)
|
## Generate an OAuth2 user token (optional)
|
||||||
|
|
||||||
If you want a user-context OAuth2 token:
|
|
||||||
1. Add `CLIENT_ID` and `CLIENT_SECRET` to your `.env`.
|
1. Add `CLIENT_ID` and `CLIENT_SECRET` to your `.env`.
|
||||||
2. Update `redirect_uri` in `generate_authtoken.py` to match your app settings.
|
2. Update `redirect_uri` in `generate_authtoken.py` to match your app settings.
|
||||||
3. Run `python generate_authtoken.py` and follow the prompts.
|
3. Run `python generate_authtoken.py` and follow the prompts.
|
||||||
4. Copy the printed access token into `.env` as `X_OAUTH_ACCESS_TOKEN`.
|
4. Copy the printed access token into `.env` as `X_OAUTH_ACCESS_TOKEN`.
|
||||||
|
If your flow returns a secret, store it as `X_OAUTH_ACCESS_TOKEN_SECRET`.
|
||||||
|
|
||||||
## Run the Grok MCP test client (optional)
|
## Run the Grok MCP test client (optional)
|
||||||
|
|
||||||
1. Set `XAI_API_KEY` in `.env`.
|
1. Set `XAI_API_KEY` in `.env`.
|
||||||
2. Make sure your MCP server is running locally (or set `MCP_SERVER_URL`).
|
2. Make sure your MCP server is running locally (or set `MCP_SERVER_URL`).
|
||||||
3. If Grok cannot reach `http://127.0.0.1:8000/mcp`, use ngrok to tunnel your
|
3. If Grok is not running on your machine, use ngrok to expose your local MCP
|
||||||
local server and point `MCP_SERVER_URL` to the public ngrok URL.
|
server and set `MCP_SERVER_URL` to the public HTTPS URL that ends with `/mcp`.
|
||||||
|
Example flow: `ngrok http 8000` then `MCP_SERVER_URL=https://<id>.ngrok-free.dev/mcp`.
|
||||||
4. Run `python test_grok_mcp.py`.
|
4. Run `python test_grok_mcp.py`.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
|
||||||
24
env.example
24
env.example
|
|
@ -1,10 +1,9 @@
|
||||||
# X API auth (fallback, optional)
|
# Required auth
|
||||||
X_OAUTH_ACCESS_TOKEN=
|
X_OAUTH_CONSUMER_KEY=
|
||||||
|
X_OAUTH_CONSUMER_SECRET=
|
||||||
X_BEARER_TOKEN=
|
X_BEARER_TOKEN=
|
||||||
|
|
||||||
# OAuth1 startup (required)
|
# OAuth1 callback
|
||||||
TWITTER_CONSUMER_KEY=
|
|
||||||
TWITTER_CONSUMER_SECRET=
|
|
||||||
X_OAUTH_CALLBACK_HOST=127.0.0.1
|
X_OAUTH_CALLBACK_HOST=127.0.0.1
|
||||||
X_OAUTH_CALLBACK_PORT=8976
|
X_OAUTH_CALLBACK_PORT=8976
|
||||||
X_OAUTH_CALLBACK_PATH=/oauth/callback
|
X_OAUTH_CALLBACK_PATH=/oauth/callback
|
||||||
|
|
@ -19,19 +18,20 @@ X_API_DEBUG=1
|
||||||
MCP_HOST=127.0.0.1
|
MCP_HOST=127.0.0.1
|
||||||
MCP_PORT=8000
|
MCP_PORT=8000
|
||||||
|
|
||||||
# FastMCP parser flag (optional)
|
|
||||||
FASTMCP_EXPERIMENTAL_ENABLE_NEW_OPENAPI_PARSER=
|
|
||||||
|
|
||||||
# Tool filtering (optional, comma-separated)
|
# Tool filtering (optional, comma-separated)
|
||||||
X_API_TOOL_TAGS=
|
|
||||||
X_API_TOOL_ALLOWLIST=
|
X_API_TOOL_ALLOWLIST=
|
||||||
X_API_TOOL_DENYLIST=
|
|
||||||
|
|
||||||
# Local Grok client (optional)
|
# Optional Grok test client
|
||||||
XAI_API_KEY=
|
XAI_API_KEY=
|
||||||
XAI_MODEL=grok-4-1-fast
|
XAI_MODEL=grok-4-1-fast
|
||||||
MCP_SERVER_URL=[your ngrok url]/mcp
|
MCP_SERVER_URL=http://127.0.0.1:8000/mcp
|
||||||
|
|
||||||
# OAuth2 token generation (optional)
|
# OAuth2 token generation (optional)
|
||||||
CLIENT_ID=
|
CLIENT_ID=
|
||||||
CLIENT_SECRET=
|
CLIENT_SECRET=
|
||||||
|
X_OAUTH_ACCESS_TOKEN=
|
||||||
|
X_OAUTH_ACCESS_TOKEN_SECRET=
|
||||||
|
|
||||||
|
# OAuth1 debug output (optional)
|
||||||
|
X_OAUTH_PRINT_TOKENS=
|
||||||
|
X_OAUTH_PRINT_AUTH_HEADER=
|
||||||
|
|
|
||||||
38
server.py
38
server.py
|
|
@ -153,11 +153,11 @@ def _wait_for_callback(
|
||||||
|
|
||||||
|
|
||||||
def run_oauth1_flow() -> tuple[str, str]:
|
def run_oauth1_flow() -> tuple[str, str]:
|
||||||
consumer_key = os.getenv("TWITTER_CONSUMER_KEY")
|
consumer_key = os.getenv("X_OAUTH_CONSUMER_KEY")
|
||||||
consumer_secret = os.getenv("TWITTER_CONSUMER_SECRET")
|
consumer_secret = os.getenv("X_OAUTH_CONSUMER_SECRET")
|
||||||
if not consumer_key or not consumer_secret:
|
if not consumer_key or not consumer_secret:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Missing TWITTER_CONSUMER_KEY or TWITTER_CONSUMER_SECRET for OAuth1 flow."
|
"Missing X_OAUTH_CONSUMER_KEY or X_OAUTH_CONSUMER_SECRET for OAuth1 flow."
|
||||||
)
|
)
|
||||||
|
|
||||||
callback_host = os.getenv("X_OAUTH_CALLBACK_HOST", "127.0.0.1")
|
callback_host = os.getenv("X_OAUTH_CALLBACK_HOST", "127.0.0.1")
|
||||||
|
|
@ -308,13 +308,16 @@ def get_auth_headers(oauth_token: str | None = None) -> dict:
|
||||||
|
|
||||||
|
|
||||||
def build_oauth1_client() -> OAuth1Client:
|
def build_oauth1_client() -> OAuth1Client:
|
||||||
consumer_key = os.getenv("TWITTER_CONSUMER_KEY")
|
consumer_key = os.getenv("X_OAUTH_CONSUMER_KEY")
|
||||||
consumer_secret = os.getenv("TWITTER_CONSUMER_SECRET")
|
consumer_secret = os.getenv("X_OAUTH_CONSUMER_SECRET")
|
||||||
if not consumer_key or not consumer_secret:
|
if not consumer_key or not consumer_secret:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Missing TWITTER_CONSUMER_KEY or TWITTER_CONSUMER_SECRET for OAuth1 signing."
|
"Missing X_OAUTH_CONSUMER_KEY or X_OAUTH_CONSUMER_SECRET for OAuth1 signing."
|
||||||
)
|
)
|
||||||
access_token, access_secret = run_oauth1_flow()
|
access_token, access_secret = run_oauth1_flow()
|
||||||
|
if is_truthy(os.getenv("X_OAUTH_PRINT_TOKENS", "0")):
|
||||||
|
print("OAuth1 access token:", access_token)
|
||||||
|
print("OAuth1 access token secret:", access_secret)
|
||||||
LOGGER.info("OAuth1 access token: %s", access_token)
|
LOGGER.info("OAuth1 access token: %s", access_token)
|
||||||
return OAuth1Client(
|
return OAuth1Client(
|
||||||
client_key=consumer_key,
|
client_key=consumer_key,
|
||||||
|
|
@ -325,6 +328,20 @@ def build_oauth1_client() -> OAuth1Client:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def print_oauth1_header_probe(oauth1_client: OAuth1Client, base_url: str) -> None:
|
||||||
|
probe_url = f"{base_url}/2/users/me"
|
||||||
|
_, signed_headers, _ = oauth1_client.sign(
|
||||||
|
probe_url,
|
||||||
|
http_method="GET",
|
||||||
|
headers={},
|
||||||
|
)
|
||||||
|
auth_header = signed_headers.get("Authorization")
|
||||||
|
if auth_header:
|
||||||
|
print("OAuth1 Authorization header (sample GET /2/users/me):", auth_header)
|
||||||
|
else:
|
||||||
|
print("OAuth1 Authorization header missing from signed probe request.")
|
||||||
|
|
||||||
|
|
||||||
def create_mcp() -> FastMCP:
|
def create_mcp() -> FastMCP:
|
||||||
load_env()
|
load_env()
|
||||||
debug_enabled = setup_logging()
|
debug_enabled = setup_logging()
|
||||||
|
|
@ -336,6 +353,9 @@ def create_mcp() -> FastMCP:
|
||||||
timeout = float(os.getenv("X_API_TIMEOUT", "30"))
|
timeout = float(os.getenv("X_API_TIMEOUT", "30"))
|
||||||
|
|
||||||
oauth1_client = build_oauth1_client()
|
oauth1_client = build_oauth1_client()
|
||||||
|
print_oauth_header = is_truthy(os.getenv("X_OAUTH_PRINT_AUTH_HEADER", "0"))
|
||||||
|
if print_oauth_header:
|
||||||
|
print_oauth1_header_probe(oauth1_client, base_url)
|
||||||
|
|
||||||
spec = load_openapi_spec()
|
spec = load_openapi_spec()
|
||||||
filtered_spec = filter_openapi_spec(spec)
|
filtered_spec = filter_openapi_spec(spec)
|
||||||
|
|
@ -390,6 +410,12 @@ def create_mcp() -> FastMCP:
|
||||||
)
|
)
|
||||||
request.url = httpx.URL(signed_url)
|
request.url = httpx.URL(signed_url)
|
||||||
request.headers.update(signed_headers)
|
request.headers.update(signed_headers)
|
||||||
|
if print_oauth_header:
|
||||||
|
auth_header = signed_headers.get("Authorization")
|
||||||
|
if auth_header:
|
||||||
|
print("OAuth1 Authorization header:", auth_header)
|
||||||
|
else:
|
||||||
|
print("OAuth1 Authorization header missing from signed request.")
|
||||||
|
|
||||||
async def log_request(request: httpx.Request) -> None:
|
async def log_request(request: httpx.Request) -> None:
|
||||||
if not debug_enabled:
|
if not debug_enabled:
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ def main() -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
chat.append(
|
chat.append(
|
||||||
user('dm @taycaldwell the message "bot.grokcommand sent this message with xMCP". If we receive an error please explicity inform us what the error is. This incldues trace ids.')
|
user('create a post saying xmcp test')
|
||||||
)
|
)
|
||||||
|
|
||||||
print("Starting chat stream...\n")
|
print("Starting chat stream...\n")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue