melchizedek-agents 1.2.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/.env.example +9 -0
  2. package/CHANGELOG.md +174 -0
  3. package/DOCUMENTATION.md +107 -10
  4. package/ONBOARDING.md +50 -11
  5. package/QUICKSTART.md +1 -1
  6. package/README.md +22 -2
  7. package/config/agents/examples/connectors.yaml +82 -0
  8. package/config/agents/syndicate.schema.json +324 -26
  9. package/dist/lib/a2a/app.js +3 -0
  10. package/dist/lib/a2a/oauthSetup.js +20 -5
  11. package/dist/lib/chatgpt/adapter.d.ts +50 -0
  12. package/dist/lib/chatgpt/adapter.js +107 -0
  13. package/dist/lib/chatgpt/cli.d.ts +23 -0
  14. package/dist/lib/chatgpt/cli.js +77 -0
  15. package/dist/lib/chatgpt/oauth.d.ts +104 -0
  16. package/dist/lib/chatgpt/oauth.js +472 -0
  17. package/dist/lib/chatgpt/state.d.ts +98 -0
  18. package/dist/lib/chatgpt/state.js +145 -0
  19. package/dist/lib/chatgpt/store.d.ts +24 -0
  20. package/dist/lib/chatgpt/store.js +94 -0
  21. package/dist/lib/compile.js +40 -25
  22. package/dist/lib/dispatch.js +6 -2
  23. package/dist/lib/doctor.d.ts +34 -3
  24. package/dist/lib/doctor.js +63 -5
  25. package/dist/lib/loadSyndicate.d.ts +6 -0
  26. package/dist/lib/mcp/server.d.ts +195 -0
  27. package/dist/lib/mcp/server.js +564 -0
  28. package/dist/lib/memory/supabaseMemoryService.js +2 -2
  29. package/dist/lib/models/adapterResolver.js +6 -1
  30. package/dist/lib/models/gateway.d.ts +1 -1
  31. package/dist/lib/models/gateway.js +4 -2
  32. package/dist/lib/models/gptAdapter.js +11 -1
  33. package/dist/lib/onboarding.d.ts +4 -3
  34. package/dist/lib/onboarding.js +48 -9
  35. package/dist/lib/runtime/syndicateTurn.js +30 -16
  36. package/dist/lib/syndicateSchema.d.ts +146 -8
  37. package/dist/lib/syndicateSchema.js +118 -11
  38. package/dist/lib/tools/credentialUses.d.ts +7 -1
  39. package/dist/lib/tools/credentialUses.js +10 -2
  40. package/dist/lib/tools/generateImageTool.js +2 -1
  41. package/dist/lib/tools/inspectImageTool.js +1 -1
  42. package/dist/lib/tools/mcpToolFactory.d.ts +29 -1
  43. package/dist/lib/tools/mcpToolFactory.js +71 -25
  44. package/dist/lib/tools/mcpTransports.d.ts +12 -0
  45. package/dist/lib/tools/mcpTransports.js +11 -0
  46. package/dist/lib/tools/oauthConsent.d.ts +18 -3
  47. package/dist/lib/tools/oauthConsent.js +49 -14
  48. package/dist/lib/tools/oauthDiscovery.d.ts +202 -0
  49. package/dist/lib/tools/oauthDiscovery.js +616 -0
  50. package/dist/lib/tools/oauthTools.d.ts +44 -12
  51. package/dist/lib/tools/oauthTools.js +79 -22
  52. package/dist/lib/tools/xApiSearchTool.js +2 -2
  53. package/dist/scripts/a2a_server.js +8 -0
  54. package/dist/scripts/assistant_worker.js +11 -1
  55. package/dist/scripts/doctor.d.ts +1 -1
  56. package/dist/scripts/doctor.js +1 -1
  57. package/dist/scripts/mcp_server.d.ts +12 -0
  58. package/dist/scripts/mcp_server.js +345 -0
  59. package/dist/scripts/setup.d.ts +2 -0
  60. package/dist/scripts/setup.js +27 -0
  61. package/dist/scripts/syndicate_chat.js +12 -3
  62. package/package.json +15 -12
  63. package/skills/README.md +2 -2
  64. package/skills/briefs/skills-readme.md +1 -1
  65. package/skills/melchizedek-onboard/SKILL.md +28 -6
  66. package/skills/melchizedek-onboard-keys/SKILL.md +17 -5
@@ -462,7 +462,7 @@
462
462
  "deprecated": true
463
463
  },
464
464
  "mcp_server_url": {
465
- "description": "MCP server (SSE) whose tools are discovered at runtime and merged with `tools`.",
465
+ "description": "MCP server (Streamable HTTP, or legacy SSE) whose tools are discovered at runtime and merged with `tools`. For more than one server, use mcp_servers.",
466
466
  "type": "string"
467
467
  },
468
468
  "mcp_tools": {
@@ -494,15 +494,20 @@
494
494
  ],
495
495
  "description": "authorization_code: each user's own token, granted through the consent pause (ADR 0085). client_credentials: the server's own token, from the token endpoint."
496
496
  },
497
+ "client_registration": {
498
+ "description": "\"dynamic\": an MCP server's authorization_code grant with no pre-issued client. The authorization server is discovered from the MCP server (RFC 9728, RFC 8414) and this deployment registers itself as a public PKCE client (RFC 7591), once per server and redirect URI, sealed in the credential store (ADR 0124). Every discovered host must be on the operator's OAuth host allowlist. MCP servers only; no client id, client secret or endpoint URL beside it.",
499
+ "type": "string",
500
+ "const": "dynamic"
501
+ },
497
502
  "authorization_url": {
498
- "description": "The provider's authorization endpoint (https). authorization_code only.",
503
+ "description": "The provider's authorization endpoint (https). authorization_code only; discovered with client_registration: dynamic.",
499
504
  "type": "string",
500
505
  "format": "uri"
501
506
  },
502
507
  "token_url": {
508
+ "description": "The provider's token endpoint (https). Required, except with client_registration: dynamic, which discovers it.",
503
509
  "type": "string",
504
- "format": "uri",
505
- "description": "The provider's token endpoint (https)."
510
+ "format": "uri"
506
511
  },
507
512
  "client_id": {
508
513
  "description": "The OAuth client id, written out (it is not a secret). Or client_id_env.",
@@ -530,7 +535,7 @@
530
535
  }
531
536
  },
532
537
  "authorization_params": {
533
- "description": "Extra authorization parameters (e.g. access_type: offline). authorization_code only; never state, redirect_uri, scope, client_id or the PKCE pair.",
538
+ "description": "Extra authorization parameters (e.g. access_type: offline). authorization_code only; never state, redirect_uri, scope, client_id, resource or the PKCE pair.",
534
539
  "type": "object",
535
540
  "propertyNames": {
536
541
  "type": "string",
@@ -544,8 +549,7 @@
544
549
  },
545
550
  "required": [
546
551
  "provider",
547
- "grant",
548
- "token_url"
552
+ "grant"
549
553
  ],
550
554
  "additionalProperties": false,
551
555
  "description": "An OAuth 2 access token sent as a bearer token on every call (lib/tools/oauthTools.ts). Secrets come from environment variable names, never values."
@@ -556,6 +560,147 @@
556
560
  ],
557
561
  "additionalProperties": false
558
562
  },
563
+ "mcp_transport": {
564
+ "description": "How mcp_server_url is reached: auto (the default), streamable_http or sse (ADR 0124). Needs mcp_server_url.",
565
+ "type": "string",
566
+ "enum": [
567
+ "auto",
568
+ "streamable_http",
569
+ "sse"
570
+ ]
571
+ },
572
+ "mcp_servers": {
573
+ "description": "Several MCP servers, each with the tools this agent may use from it, its grant and its transport (ADR 0124). Cannot be combined with mcp_server_url.",
574
+ "minItems": 1,
575
+ "maxItems": 16,
576
+ "type": "array",
577
+ "items": {
578
+ "type": "object",
579
+ "properties": {
580
+ "name": {
581
+ "type": "string",
582
+ "pattern": "^[a-z][a-z0-9_-]{0,63}$",
583
+ "description": "A short name for the server, unique on this agent: logs and errors name it."
584
+ },
585
+ "url": {
586
+ "type": "string",
587
+ "format": "uri",
588
+ "description": "The MCP server's URL: its Streamable HTTP endpoint, or a legacy SSE endpoint. Checked by the SSRF guard."
589
+ },
590
+ "tools": {
591
+ "minItems": 1,
592
+ "type": "array",
593
+ "items": {
594
+ "type": "string",
595
+ "minLength": 1
596
+ },
597
+ "description": "The server's tools this agent may use, by name; any other tool it lists is not exposed. A name may appear on one server only, and never among the agent's own tools. require_approval may name these."
598
+ },
599
+ "auth": {
600
+ "description": "The OAuth grant this server takes (ADR 0112, ADR 0124). Without it, MCP_BEARER_TOKENS applies.",
601
+ "type": "object",
602
+ "properties": {
603
+ "oauth2": {
604
+ "type": "object",
605
+ "properties": {
606
+ "provider": {
607
+ "type": "string",
608
+ "pattern": "^[a-z0-9][a-z0-9._-]{0,63}$",
609
+ "description": "The credential store's name for the provider (e.g. github). Every tool naming it shares one grant."
610
+ },
611
+ "grant": {
612
+ "type": "string",
613
+ "enum": [
614
+ "authorization_code",
615
+ "client_credentials"
616
+ ],
617
+ "description": "authorization_code: each user's own token, granted through the consent pause (ADR 0085). client_credentials: the server's own token, from the token endpoint."
618
+ },
619
+ "client_registration": {
620
+ "description": "\"dynamic\": an MCP server's authorization_code grant with no pre-issued client. The authorization server is discovered from the MCP server (RFC 9728, RFC 8414) and this deployment registers itself as a public PKCE client (RFC 7591), once per server and redirect URI, sealed in the credential store (ADR 0124). Every discovered host must be on the operator's OAuth host allowlist. MCP servers only; no client id, client secret or endpoint URL beside it.",
621
+ "type": "string",
622
+ "const": "dynamic"
623
+ },
624
+ "authorization_url": {
625
+ "description": "The provider's authorization endpoint (https). authorization_code only; discovered with client_registration: dynamic.",
626
+ "type": "string",
627
+ "format": "uri"
628
+ },
629
+ "token_url": {
630
+ "description": "The provider's token endpoint (https). Required, except with client_registration: dynamic, which discovers it.",
631
+ "type": "string",
632
+ "format": "uri"
633
+ },
634
+ "client_id": {
635
+ "description": "The OAuth client id, written out (it is not a secret). Or client_id_env.",
636
+ "type": "string",
637
+ "minLength": 1,
638
+ "maxLength": 512
639
+ },
640
+ "client_id_env": {
641
+ "description": "Environment variable holding the client id. Or client_id.",
642
+ "type": "string",
643
+ "pattern": "^[A-Z_][A-Z0-9_]*$"
644
+ },
645
+ "client_secret_env": {
646
+ "description": "Environment variable NAME holding the client secret, never the value. Required for client_credentials; omit for a public client (PKCE alone).",
647
+ "type": "string",
648
+ "pattern": "^[A-Z_][A-Z0-9_]*$"
649
+ },
650
+ "scopes": {
651
+ "description": "The scopes the grant asks for.",
652
+ "maxItems": 64,
653
+ "type": "array",
654
+ "items": {
655
+ "type": "string",
656
+ "pattern": "^[\\x21\\x23-\\x5b\\x5d-\\x7e]{1,256}$"
657
+ }
658
+ },
659
+ "authorization_params": {
660
+ "description": "Extra authorization parameters (e.g. access_type: offline). authorization_code only; never state, redirect_uri, scope, client_id, resource or the PKCE pair.",
661
+ "type": "object",
662
+ "propertyNames": {
663
+ "type": "string",
664
+ "pattern": "^[A-Za-z0-9_.-]{1,64}$"
665
+ },
666
+ "additionalProperties": {
667
+ "type": "string",
668
+ "maxLength": 512
669
+ }
670
+ }
671
+ },
672
+ "required": [
673
+ "provider",
674
+ "grant"
675
+ ],
676
+ "additionalProperties": false,
677
+ "description": "An OAuth 2 access token sent as a bearer token on every call (lib/tools/oauthTools.ts). Secrets come from environment variable names, never values."
678
+ }
679
+ },
680
+ "required": [
681
+ "oauth2"
682
+ ],
683
+ "additionalProperties": false
684
+ },
685
+ "transport": {
686
+ "type": "string",
687
+ "enum": [
688
+ "auto",
689
+ "streamable_http",
690
+ "sse"
691
+ ],
692
+ "description": "How the MCP server is reached: auto (the default) tries Streamable HTTP and falls back to the legacy SSE transport on the spec's signal; streamable_http or sse use that one only (ADR 0124)."
693
+ }
694
+ },
695
+ "required": [
696
+ "name",
697
+ "url",
698
+ "tools"
699
+ ],
700
+ "additionalProperties": false,
701
+ "description": "One MCP server among several (ADR 0124)."
702
+ }
703
+ },
559
704
  "skills": {
560
705
  "type": "object",
561
706
  "properties": {
@@ -685,15 +830,20 @@
685
830
  ],
686
831
  "description": "authorization_code: each user's own token, granted through the consent pause (ADR 0085). client_credentials: the server's own token, from the token endpoint."
687
832
  },
833
+ "client_registration": {
834
+ "description": "\"dynamic\": an MCP server's authorization_code grant with no pre-issued client. The authorization server is discovered from the MCP server (RFC 9728, RFC 8414) and this deployment registers itself as a public PKCE client (RFC 7591), once per server and redirect URI, sealed in the credential store (ADR 0124). Every discovered host must be on the operator's OAuth host allowlist. MCP servers only; no client id, client secret or endpoint URL beside it.",
835
+ "type": "string",
836
+ "const": "dynamic"
837
+ },
688
838
  "authorization_url": {
689
- "description": "The provider's authorization endpoint (https). authorization_code only.",
839
+ "description": "The provider's authorization endpoint (https). authorization_code only; discovered with client_registration: dynamic.",
690
840
  "type": "string",
691
841
  "format": "uri"
692
842
  },
693
843
  "token_url": {
844
+ "description": "The provider's token endpoint (https). Required, except with client_registration: dynamic, which discovers it.",
694
845
  "type": "string",
695
- "format": "uri",
696
- "description": "The provider's token endpoint (https)."
846
+ "format": "uri"
697
847
  },
698
848
  "client_id": {
699
849
  "description": "The OAuth client id, written out (it is not a secret). Or client_id_env.",
@@ -721,7 +871,7 @@
721
871
  }
722
872
  },
723
873
  "authorization_params": {
724
- "description": "Extra authorization parameters (e.g. access_type: offline). authorization_code only; never state, redirect_uri, scope, client_id or the PKCE pair.",
874
+ "description": "Extra authorization parameters (e.g. access_type: offline). authorization_code only; never state, redirect_uri, scope, client_id, resource or the PKCE pair.",
725
875
  "type": "object",
726
876
  "propertyNames": {
727
877
  "type": "string",
@@ -735,8 +885,7 @@
735
885
  },
736
886
  "required": [
737
887
  "provider",
738
- "grant",
739
- "token_url"
888
+ "grant"
740
889
  ],
741
890
  "additionalProperties": false,
742
891
  "description": "An OAuth 2 access token sent as a bearer token on every call (lib/tools/oauthTools.ts). Secrets come from environment variable names, never values."
@@ -1300,7 +1449,7 @@
1300
1449
  "deprecated": true
1301
1450
  },
1302
1451
  "mcp_server_url": {
1303
- "description": "MCP server (SSE) whose tools are discovered at runtime and merged with `tools`.",
1452
+ "description": "MCP server (Streamable HTTP, or legacy SSE) whose tools are discovered at runtime and merged with `tools`. For more than one server, use mcp_servers.",
1304
1453
  "type": "string"
1305
1454
  },
1306
1455
  "mcp_tools": {
@@ -1332,15 +1481,20 @@
1332
1481
  ],
1333
1482
  "description": "authorization_code: each user's own token, granted through the consent pause (ADR 0085). client_credentials: the server's own token, from the token endpoint."
1334
1483
  },
1484
+ "client_registration": {
1485
+ "description": "\"dynamic\": an MCP server's authorization_code grant with no pre-issued client. The authorization server is discovered from the MCP server (RFC 9728, RFC 8414) and this deployment registers itself as a public PKCE client (RFC 7591), once per server and redirect URI, sealed in the credential store (ADR 0124). Every discovered host must be on the operator's OAuth host allowlist. MCP servers only; no client id, client secret or endpoint URL beside it.",
1486
+ "type": "string",
1487
+ "const": "dynamic"
1488
+ },
1335
1489
  "authorization_url": {
1336
- "description": "The provider's authorization endpoint (https). authorization_code only.",
1490
+ "description": "The provider's authorization endpoint (https). authorization_code only; discovered with client_registration: dynamic.",
1337
1491
  "type": "string",
1338
1492
  "format": "uri"
1339
1493
  },
1340
1494
  "token_url": {
1495
+ "description": "The provider's token endpoint (https). Required, except with client_registration: dynamic, which discovers it.",
1341
1496
  "type": "string",
1342
- "format": "uri",
1343
- "description": "The provider's token endpoint (https)."
1497
+ "format": "uri"
1344
1498
  },
1345
1499
  "client_id": {
1346
1500
  "description": "The OAuth client id, written out (it is not a secret). Or client_id_env.",
@@ -1368,7 +1522,7 @@
1368
1522
  }
1369
1523
  },
1370
1524
  "authorization_params": {
1371
- "description": "Extra authorization parameters (e.g. access_type: offline). authorization_code only; never state, redirect_uri, scope, client_id or the PKCE pair.",
1525
+ "description": "Extra authorization parameters (e.g. access_type: offline). authorization_code only; never state, redirect_uri, scope, client_id, resource or the PKCE pair.",
1372
1526
  "type": "object",
1373
1527
  "propertyNames": {
1374
1528
  "type": "string",
@@ -1382,8 +1536,7 @@
1382
1536
  },
1383
1537
  "required": [
1384
1538
  "provider",
1385
- "grant",
1386
- "token_url"
1539
+ "grant"
1387
1540
  ],
1388
1541
  "additionalProperties": false,
1389
1542
  "description": "An OAuth 2 access token sent as a bearer token on every call (lib/tools/oauthTools.ts). Secrets come from environment variable names, never values."
@@ -1394,6 +1547,147 @@
1394
1547
  ],
1395
1548
  "additionalProperties": false
1396
1549
  },
1550
+ "mcp_transport": {
1551
+ "description": "How mcp_server_url is reached: auto (the default), streamable_http or sse (ADR 0124). Needs mcp_server_url.",
1552
+ "type": "string",
1553
+ "enum": [
1554
+ "auto",
1555
+ "streamable_http",
1556
+ "sse"
1557
+ ]
1558
+ },
1559
+ "mcp_servers": {
1560
+ "description": "Several MCP servers, each with the tools this agent may use from it, its grant and its transport (ADR 0124). Cannot be combined with mcp_server_url.",
1561
+ "minItems": 1,
1562
+ "maxItems": 16,
1563
+ "type": "array",
1564
+ "items": {
1565
+ "type": "object",
1566
+ "properties": {
1567
+ "name": {
1568
+ "type": "string",
1569
+ "pattern": "^[a-z][a-z0-9_-]{0,63}$",
1570
+ "description": "A short name for the server, unique on this agent: logs and errors name it."
1571
+ },
1572
+ "url": {
1573
+ "type": "string",
1574
+ "format": "uri",
1575
+ "description": "The MCP server's URL: its Streamable HTTP endpoint, or a legacy SSE endpoint. Checked by the SSRF guard."
1576
+ },
1577
+ "tools": {
1578
+ "minItems": 1,
1579
+ "type": "array",
1580
+ "items": {
1581
+ "type": "string",
1582
+ "minLength": 1
1583
+ },
1584
+ "description": "The server's tools this agent may use, by name; any other tool it lists is not exposed. A name may appear on one server only, and never among the agent's own tools. require_approval may name these."
1585
+ },
1586
+ "auth": {
1587
+ "description": "The OAuth grant this server takes (ADR 0112, ADR 0124). Without it, MCP_BEARER_TOKENS applies.",
1588
+ "type": "object",
1589
+ "properties": {
1590
+ "oauth2": {
1591
+ "type": "object",
1592
+ "properties": {
1593
+ "provider": {
1594
+ "type": "string",
1595
+ "pattern": "^[a-z0-9][a-z0-9._-]{0,63}$",
1596
+ "description": "The credential store's name for the provider (e.g. github). Every tool naming it shares one grant."
1597
+ },
1598
+ "grant": {
1599
+ "type": "string",
1600
+ "enum": [
1601
+ "authorization_code",
1602
+ "client_credentials"
1603
+ ],
1604
+ "description": "authorization_code: each user's own token, granted through the consent pause (ADR 0085). client_credentials: the server's own token, from the token endpoint."
1605
+ },
1606
+ "client_registration": {
1607
+ "description": "\"dynamic\": an MCP server's authorization_code grant with no pre-issued client. The authorization server is discovered from the MCP server (RFC 9728, RFC 8414) and this deployment registers itself as a public PKCE client (RFC 7591), once per server and redirect URI, sealed in the credential store (ADR 0124). Every discovered host must be on the operator's OAuth host allowlist. MCP servers only; no client id, client secret or endpoint URL beside it.",
1608
+ "type": "string",
1609
+ "const": "dynamic"
1610
+ },
1611
+ "authorization_url": {
1612
+ "description": "The provider's authorization endpoint (https). authorization_code only; discovered with client_registration: dynamic.",
1613
+ "type": "string",
1614
+ "format": "uri"
1615
+ },
1616
+ "token_url": {
1617
+ "description": "The provider's token endpoint (https). Required, except with client_registration: dynamic, which discovers it.",
1618
+ "type": "string",
1619
+ "format": "uri"
1620
+ },
1621
+ "client_id": {
1622
+ "description": "The OAuth client id, written out (it is not a secret). Or client_id_env.",
1623
+ "type": "string",
1624
+ "minLength": 1,
1625
+ "maxLength": 512
1626
+ },
1627
+ "client_id_env": {
1628
+ "description": "Environment variable holding the client id. Or client_id.",
1629
+ "type": "string",
1630
+ "pattern": "^[A-Z_][A-Z0-9_]*$"
1631
+ },
1632
+ "client_secret_env": {
1633
+ "description": "Environment variable NAME holding the client secret, never the value. Required for client_credentials; omit for a public client (PKCE alone).",
1634
+ "type": "string",
1635
+ "pattern": "^[A-Z_][A-Z0-9_]*$"
1636
+ },
1637
+ "scopes": {
1638
+ "description": "The scopes the grant asks for.",
1639
+ "maxItems": 64,
1640
+ "type": "array",
1641
+ "items": {
1642
+ "type": "string",
1643
+ "pattern": "^[\\x21\\x23-\\x5b\\x5d-\\x7e]{1,256}$"
1644
+ }
1645
+ },
1646
+ "authorization_params": {
1647
+ "description": "Extra authorization parameters (e.g. access_type: offline). authorization_code only; never state, redirect_uri, scope, client_id, resource or the PKCE pair.",
1648
+ "type": "object",
1649
+ "propertyNames": {
1650
+ "type": "string",
1651
+ "pattern": "^[A-Za-z0-9_.-]{1,64}$"
1652
+ },
1653
+ "additionalProperties": {
1654
+ "type": "string",
1655
+ "maxLength": 512
1656
+ }
1657
+ }
1658
+ },
1659
+ "required": [
1660
+ "provider",
1661
+ "grant"
1662
+ ],
1663
+ "additionalProperties": false,
1664
+ "description": "An OAuth 2 access token sent as a bearer token on every call (lib/tools/oauthTools.ts). Secrets come from environment variable names, never values."
1665
+ }
1666
+ },
1667
+ "required": [
1668
+ "oauth2"
1669
+ ],
1670
+ "additionalProperties": false
1671
+ },
1672
+ "transport": {
1673
+ "type": "string",
1674
+ "enum": [
1675
+ "auto",
1676
+ "streamable_http",
1677
+ "sse"
1678
+ ],
1679
+ "description": "How the MCP server is reached: auto (the default) tries Streamable HTTP and falls back to the legacy SSE transport on the spec's signal; streamable_http or sse use that one only (ADR 0124)."
1680
+ }
1681
+ },
1682
+ "required": [
1683
+ "name",
1684
+ "url",
1685
+ "tools"
1686
+ ],
1687
+ "additionalProperties": false,
1688
+ "description": "One MCP server among several (ADR 0124)."
1689
+ }
1690
+ },
1397
1691
  "skills": {
1398
1692
  "type": "object",
1399
1693
  "properties": {
@@ -1523,15 +1817,20 @@
1523
1817
  ],
1524
1818
  "description": "authorization_code: each user's own token, granted through the consent pause (ADR 0085). client_credentials: the server's own token, from the token endpoint."
1525
1819
  },
1820
+ "client_registration": {
1821
+ "description": "\"dynamic\": an MCP server's authorization_code grant with no pre-issued client. The authorization server is discovered from the MCP server (RFC 9728, RFC 8414) and this deployment registers itself as a public PKCE client (RFC 7591), once per server and redirect URI, sealed in the credential store (ADR 0124). Every discovered host must be on the operator's OAuth host allowlist. MCP servers only; no client id, client secret or endpoint URL beside it.",
1822
+ "type": "string",
1823
+ "const": "dynamic"
1824
+ },
1526
1825
  "authorization_url": {
1527
- "description": "The provider's authorization endpoint (https). authorization_code only.",
1826
+ "description": "The provider's authorization endpoint (https). authorization_code only; discovered with client_registration: dynamic.",
1528
1827
  "type": "string",
1529
1828
  "format": "uri"
1530
1829
  },
1531
1830
  "token_url": {
1831
+ "description": "The provider's token endpoint (https). Required, except with client_registration: dynamic, which discovers it.",
1532
1832
  "type": "string",
1533
- "format": "uri",
1534
- "description": "The provider's token endpoint (https)."
1833
+ "format": "uri"
1535
1834
  },
1536
1835
  "client_id": {
1537
1836
  "description": "The OAuth client id, written out (it is not a secret). Or client_id_env.",
@@ -1559,7 +1858,7 @@
1559
1858
  }
1560
1859
  },
1561
1860
  "authorization_params": {
1562
- "description": "Extra authorization parameters (e.g. access_type: offline). authorization_code only; never state, redirect_uri, scope, client_id or the PKCE pair.",
1861
+ "description": "Extra authorization parameters (e.g. access_type: offline). authorization_code only; never state, redirect_uri, scope, client_id, resource or the PKCE pair.",
1563
1862
  "type": "object",
1564
1863
  "propertyNames": {
1565
1864
  "type": "string",
@@ -1573,8 +1872,7 @@
1573
1872
  },
1574
1873
  "required": [
1575
1874
  "provider",
1576
- "grant",
1577
- "token_url"
1875
+ "grant"
1578
1876
  ],
1579
1877
  "additionalProperties": false,
1580
1878
  "description": "An OAuth 2 access token sent as a bearer token on every call (lib/tools/oauthTools.ts). Secrets come from environment variable names, never values."
@@ -46,6 +46,7 @@ import { duplicateInterfacesForLegacy } from '@a2a-js/sdk/compat/v0_3';
46
46
  import { agentCardHandler, jsonRpcHandler, restHandler } from '@a2a-js/sdk/server/express';
47
47
  import { InProcessSessionService } from '../runtime/sessions.js';
48
48
  import { runtimeSetting } from '../runtime/runtimeFlag.js';
49
+ import { refuseChatGptSignInOnServedSurface } from '../chatgpt/state.js';
49
50
  import { geminiAdapterSetting } from '../models/adapterResolver.js';
50
51
  import { loadSyndicate, loadSyndicateFromRegistry, nestedLoader } from '../loadSyndicate.js';
51
52
  import { providerForModel, resolveModel } from '../models/registry.js';
@@ -264,6 +265,8 @@ export async function createA2AApp(options) {
264
265
  // A deployment still configured for the removed adk runtime fails here, naming 1.0.0 (ADR 0107).
265
266
  runtimeSetting();
266
267
  geminiAdapterSetting();
268
+ // Sign in with ChatGPT is local only (ADR 0126): a served app never runs OpenAI ids on it.
269
+ refuseChatGptSignInOnServedSurface('The A2A server');
267
270
  const log = options.log ?? ((m) => console.log(`[A2A] ${m}`));
268
271
  const warn = options.warn ?? ((m) => console.warn(`[A2A] ⚠ ${m}`));
269
272
  const bindings = options.bindings ?? (() => ({
@@ -26,8 +26,9 @@
26
26
  import { CREDENTIAL_KEY_ENV, credentialCipherFromEnv } from '../tools/credentialCipher.js';
27
27
  import { credentialStore, memoryCredentialRows } from '../tools/credentialStore.js';
28
28
  import { oauthConsent } from '../tools/oauthConsent.js';
29
+ import { dynamicOAuthClient, oauthClientRegistry } from '../tools/oauthDiscovery.js';
29
30
  import { OAUTH_HOSTS_ENV, oauthHosts } from '../tools/oauthHosts.js';
30
- import { oauthClientsFor, oauthGrantHostProblems, oauthRefreshProviders, syndicateOAuthGrants, syndicateOAuthHostProblems } from '../tools/oauthTools.js';
31
+ import { dynamicOAuthGrantsFor, oauthClientsFor, oauthGrantHostProblems, oauthRefreshProviders, syndicateOAuthGrants, syndicateOAuthHostProblems } from '../tools/oauthTools.js';
31
32
  import { CREDENTIAL_HOSTS_ENV, credentialHosts } from '../tools/credentialHosts.js';
32
33
  import { syndicateCredentialHostProblems, syndicateCredentialUses, unboundCredentialEnvs } from '../tools/credentialUses.js';
33
34
  /** The consent callback's URL, exactly as registered at every provider. */
@@ -224,11 +225,25 @@ export function oauthServerSetup(options) {
224
225
  return { summary: `off (${CREDENTIAL_KEY_ENV} unset) · ${hostsLabel}`, credentialSummary, warnings };
225
226
  }
226
227
  const clients = oauthClientsFor(options.configs, { env, ...(options.load ? { load: options.load } : {}), allowlist: allowlist ?? null });
227
- const providers = Object.keys(clients);
228
+ const rows = options.rows ?? memoryCredentialRows();
229
+ // A grant with client_registration: dynamic (ADR 0124): its client is
230
+ // discovered and registered when first needed, for the configured redirect
231
+ // URI, and kept sealed beside the users' tokens. Without a redirect URI it
232
+ // cannot register, and is named in the warning below.
233
+ const dynamicGrants = dynamicOAuthGrantsFor(options.configs, { env, ...(options.load ? { load: options.load } : {}), allowlist: allowlist ?? null });
234
+ const registry = oauthClientRegistry({ rows, cipher });
235
+ const dynamicClients = redirect
236
+ ? Object.fromEntries(Object.entries(dynamicGrants).map(([name, grant]) => [
237
+ name,
238
+ dynamicOAuthClient(grant, { redirectUri: redirect, registry, ...(options.allowPrivate ? { allowPrivate: true } : {}), ...(options.fetch ? { fetch: options.fetch } : {}) }),
239
+ ]))
240
+ : {};
241
+ const allClients = { ...clients, ...dynamicClients };
242
+ const providers = [...Object.keys(clients), ...Object.keys(dynamicGrants)];
228
243
  const store = credentialStore({
229
- rows: options.rows ?? memoryCredentialRows(),
244
+ rows,
230
245
  cipher,
231
- providers: oauthRefreshProviders(clients, { ...(options.allowPrivate ? { allowPrivate: true } : {}), ...(options.fetch ? { fetch: options.fetch } : {}) }),
246
+ providers: oauthRefreshProviders(allClients, { ...(options.allowPrivate ? { allowPrivate: true } : {}), ...(options.fetch ? { fetch: options.fetch } : {}) }),
232
247
  ...(options.audit ? { audit: options.audit } : {}),
233
248
  });
234
249
  if (!options.rows)
@@ -244,7 +259,7 @@ export function oauthServerSetup(options) {
244
259
  return { toolCredentials: { store }, summary: `sealed (key ${cipher.keyId}) in ${where}; no consent step · ${hostsLabel}`, credentialSummary, warnings };
245
260
  }
246
261
  const consent = oauthConsent({
247
- providers: clients,
262
+ providers: allClients,
248
263
  redirectUri: redirect,
249
264
  credentials: store,
250
265
  ...(options.audit ? { audit: options.audit } : {}),
@@ -0,0 +1,50 @@
1
+ /**
2
+ * lib/chatgpt/adapter.ts — GPT on the person's ChatGPT plan, through Sign in
3
+ * with ChatGPT, on their own machine only (ADR 0126).
4
+ *
5
+ * It is GptAdapter (the Responses API) with a different credential and the
6
+ * request shape OpenAI documents for plan usage
7
+ * (developers.openai.com/siwc/token-sharing-open-source/models-and-inference
8
+ * and …/preview-limitations):
9
+ * - always `POST https://api.openai.com/v1/responses`, never a proxy or a
10
+ * platform, with the stored access token as the bearer, refreshed as it
11
+ * nears expiry (lib/chatgpt/oauth.ts freshAccessToken), and no
12
+ * organization or project header;
13
+ * - `stream: true` and `store: false` on every request; a call that did
14
+ * not ask to stream is streamed and folded back into one final;
15
+ * - none of the parameters the preview refuses: `max_output_tokens`,
16
+ * `temperature`, `top_p`;
17
+ * - no SDK retries: a plan-usage limit (429) is reported, not hammered.
18
+ * The provider id stays `openai`, so reasoning replay and the stored events
19
+ * are the same as on a key.
20
+ *
21
+ * On a served surface (lib/chatgpt/state.ts markServedSurface) every call
22
+ * ends in a CHATGPT_SIGNIN_LOCAL_ONLY final before any token is read.
23
+ */
24
+ import type { ModelRequest, ModelResponse } from '../models/contract.ts';
25
+ import type { ProviderEndpoint } from '../models/endpoints.ts';
26
+ import { GptAdapter } from '../models/gptAdapter.ts';
27
+ import type { OAuthOptions } from './oauth.ts';
28
+ export interface ChatGptSignInAdapterOptions extends OAuthOptions {
29
+ model: string;
30
+ /** The credential file; default signInFile(). */
31
+ file?: string;
32
+ }
33
+ export declare class ChatGptSignInAdapter extends GptAdapter {
34
+ #private;
35
+ protected readonly label: string;
36
+ constructor(options: ChatGptSignInAdapterOptions);
37
+ protected endpoint(): ProviderEndpoint;
38
+ protected baseURL(): string;
39
+ protected missingKeyMessage(): string;
40
+ /** The token source: read (and refreshed) on every request, never held in the adapter. */
41
+ protected clientAuth(): {
42
+ apiKey: () => Promise<string>;
43
+ baseURL: string;
44
+ };
45
+ protected clientOptions(): Record<string, unknown>;
46
+ /** The preview takes none of the sampling fields. */
47
+ protected acceptsSampling(): boolean;
48
+ requestBody(request: ModelRequest, model?: string, endpoint?: ProviderEndpoint): Record<string, unknown>;
49
+ generate(request: ModelRequest): AsyncGenerator<ModelResponse, void>;
50
+ }