curl --request POST \
--url https://guidinghand.ai/v1/mcp_oauth/start \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"url": "https://mcp.linear.app/mcp",
"secret_id": "<string>",
"client_id": "<string>",
"client_secret": "<string>",
"name": "<string>",
"connector": "notion"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
url: 'https://mcp.linear.app/mcp',
secret_id: '<string>',
client_id: '<string>',
client_secret: '<string>',
name: '<string>',
connector: 'notion'
})
};
fetch('https://guidinghand.ai/v1/mcp_oauth/start', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://guidinghand.ai/v1/mcp_oauth/start"
payload = {
"url": "https://mcp.linear.app/mcp",
"secret_id": "<string>",
"client_id": "<string>",
"client_secret": "<string>",
"name": "<string>",
"connector": "notion"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"object": "mcp_oauth",
"secret_id": "tsec_8Zp2kQ7wLm4x",
"url": "<string>",
"status": "pending",
"pending": true,
"scope": "<string>",
"connected_at": "2023-11-07T05:31:56Z",
"error": "<string>",
"authorize_url": "<string>"
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"code": "<string>",
"test_case_ids": [
"<string>"
],
"decided_by": {
"type": "person",
"email": "<string>",
"key_id": "<string>"
},
"conversation_id": "<string>",
"agent_id": "<string>",
"approval_id": "<string>",
"question_id": "<string>"
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"code": "<string>",
"test_case_ids": [
"<string>"
],
"decided_by": {
"type": "person",
"email": "<string>",
"key_id": "<string>"
},
"conversation_id": "<string>",
"agent_id": "<string>",
"approval_id": "<string>",
"question_id": "<string>"
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"code": "<string>",
"test_case_ids": [
"<string>"
],
"decided_by": {
"type": "person",
"email": "<string>",
"key_id": "<string>"
},
"conversation_id": "<string>",
"agent_id": "<string>",
"approval_id": "<string>",
"question_id": "<string>"
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"code": "<string>",
"test_case_ids": [
"<string>"
],
"decided_by": {
"type": "person",
"email": "<string>",
"key_id": "<string>"
},
"conversation_id": "<string>",
"agent_id": "<string>",
"approval_id": "<string>",
"question_id": "<string>"
}
}Start an OAuth sign-in for an MCP server
Connects an MCP server that signs in with OAuth (the MCP authorization spec, 2025-06-18): Eager finds its authorization server (protected-resource and authorization-server metadata), registers itself as a client when the server allows it, and returns the provider’s sign-in page (PKCE). The person opens it, signs in and is sent back to /v1/mcp_oauth/callback, which finishes the sign-in. The console’s Connect with OAuth does all of this. Needs a signed-in person with the admin role (API keys get a 403): only the person who started a sign-in can finish it. Then save the server with auth: { "secret_id": … }. Tokens are kept encrypted, renewed before they expire, and never returned.
curl --request POST \
--url https://guidinghand.ai/v1/mcp_oauth/start \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"url": "https://mcp.linear.app/mcp",
"secret_id": "<string>",
"client_id": "<string>",
"client_secret": "<string>",
"name": "<string>",
"connector": "notion"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
url: 'https://mcp.linear.app/mcp',
secret_id: '<string>',
client_id: '<string>',
client_secret: '<string>',
name: '<string>',
connector: 'notion'
})
};
fetch('https://guidinghand.ai/v1/mcp_oauth/start', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://guidinghand.ai/v1/mcp_oauth/start"
payload = {
"url": "https://mcp.linear.app/mcp",
"secret_id": "<string>",
"client_id": "<string>",
"client_secret": "<string>",
"name": "<string>",
"connector": "notion"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"object": "mcp_oauth",
"secret_id": "tsec_8Zp2kQ7wLm4x",
"url": "<string>",
"status": "pending",
"pending": true,
"scope": "<string>",
"connected_at": "2023-11-07T05:31:56Z",
"error": "<string>",
"authorize_url": "<string>"
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"code": "<string>",
"test_case_ids": [
"<string>"
],
"decided_by": {
"type": "person",
"email": "<string>",
"key_id": "<string>"
},
"conversation_id": "<string>",
"agent_id": "<string>",
"approval_id": "<string>",
"question_id": "<string>"
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"code": "<string>",
"test_case_ids": [
"<string>"
],
"decided_by": {
"type": "person",
"email": "<string>",
"key_id": "<string>"
},
"conversation_id": "<string>",
"agent_id": "<string>",
"approval_id": "<string>",
"question_id": "<string>"
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"code": "<string>",
"test_case_ids": [
"<string>"
],
"decided_by": {
"type": "person",
"email": "<string>",
"key_id": "<string>"
},
"conversation_id": "<string>",
"agent_id": "<string>",
"approval_id": "<string>",
"question_id": "<string>"
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"code": "<string>",
"test_case_ids": [
"<string>"
],
"decided_by": {
"type": "person",
"email": "<string>",
"key_id": "<string>"
},
"conversation_id": "<string>",
"agent_id": "<string>",
"approval_id": "<string>",
"question_id": "<string>"
}
}Authorizations
An org API key (gh_live_…) from Settings → API keys in the console.
Body
The MCP server's Streamable HTTP address.
"https://mcp.linear.app/mcp"
An OAuth sign-in to reconnect (it keeps its id, so every agent version that uses it works again).
For a server that doesn't let apps register themselves (a 400 with code: mcp_oauth_client_needed and the redirect_uri to register): the client you registered with it.
That client's secret, if it has one. Kept encrypted.
For a new sign-in: how the org's connections list it, like "Notion". Left out: the library entry's name, else the server's host.
80For a new sign-in: the library entry it's for (GET /v1/connectors).
"notion"
Response
OK
An OAuth sign-in for an MCP server: its state, never its tokens.
mcp_oauth Put it in the server's auth ({ "secret_id": … }) to use this sign-in.
"tsec_8Zp2kQ7wLm4x"
The MCP server it's for. It's only ever sent to this address's origin.
pending: not connected yet. connected: the agent can call the server. expired: the provider wouldn't renew it; reconnect it.
pending, connected, expired A sign-in started (or a reconnect) waits for the person to finish it.
The scopes it was granted (or asked for).
Why the last attempt didn't connect, or why it expired.
Start only: the provider's sign-in page, to open in a popup for the person.